* Add Kotlin snippet for VertexAiSessionService
The VertexAiSessionService section showed Python, Go and Java. Kotlin gained
the service in adk-kotlin 0.7.0, so add a Kotlin tab and Kotlin to that
section's language-support badge.
Kotlin addresses the reasoning engine differently from every sibling tab on
the page, so the snippet says so at the point of use:
- The engine is fixed at construction via `reasoningEngineId`. The 0.7.0 KDoc
is explicit that, unlike the Python and Java ADK, `SessionKey.appName` is
never parsed to derive the engine -- it is only a label. The Python tab
above passes the engine through `app_name` on each call.
- `reasoningEngineId` must be the bare numeric id. The constructor rejects a
full resource name outright (`require(reasoningEngineId.all { it.isDigit() })`),
while the Python tab passes
`projects/.../locations/.../reasoningEngines/...`.
A reader copying the adjacent Python idiom would therefore fail twice over.
Written inline to match the two existing Kotlin tabs on this page. Inline
snippets never reach Gradle, so this one was additionally compiled against the
0.7.0 pin in a scratch file that is not part of the commit.
* Address review: JVM-only note, wire the service to a Runner
Five fixes from self-review against the 0.7.0 sources and upstream's own
VertexAiSessionServiceExample.kt, which I should have consulted before writing
the first version:
- State that the service is JVM-only. It lives in core/src/jvmMain, so it does
not exist on Android. Kotlin is the only language on this page where that
distinction applies, so if the Kotlin tab omits it, nothing carries it.
- Show the service actually being used. The snippet stopped at an uncalled
`suspend fun`; it now hands the service to an InMemoryRunner, which is what
the section is about and what the upstream example does.
- Use `runBlocking` in a `main`, matching upstream, instead of a suspend
function nothing calls.
- Drop `state = mapOf(...)`. It defaults to null, upstream omits it, and the
Java tab explicitly notes no initial state is needed, so it introduced a
concept the sibling tabs deliberately avoid.
- Widen the comparison from "the Python and Java tabs" to all the other tabs.
The KDoc phrasing names Python and Java, but this page also has a Go tab.
The added LlmAgent needs an explicit `model`; the first draft would not have
compiled without it, which the scratch compile caught.
* Trim the snippet back to parity with the sibling tabs
The previous revision added an LlmAgent, a Gemini model and an InMemoryRunner,
taking the tab to 30 code lines against Python's 5, Go's 8 and Java's 12. No
sibling tab on this page constructs a runner or an agent, and this section's
prose never mentions one -- it is a characteristics list, not a wiring guide.
That change came from misapplying a review finding. On sessions/memory the
equivalent note was right: the prose there says "instantiating the
VertexAiMemoryBankService and passing it to the Runner" and the Python tab
shows exactly that. Neither holds here, so the runner was answering a question
this page does not ask.
Now scoped like the Java tab, the closest analogue: construct the service, then
create one session. That is still enough to demonstrate both divergences -- the
engine pinned at construction as a bare numeric id, and appName being only a
label -- since showing the second requires a SessionKey.
`runBlocking` stays, because createSession is a suspend function; it is the
direct counterpart of the Java tab's `.blockingGet()`, and the comment now says
so. Recompiled against the 0.7.0 pin in a scratch file.
* docs: update experimental annotation placement and refine context caching documentation note
19 KiB
Session: Tracking individual conversations
A Session represents a single conversation thread between a user and your
agent. Just like you wouldn't start every text message from scratch, agents need
context regarding the ongoing interaction. The Session object in ADK is
designed specifically to track and manage these individual conversation threads.
Session objects
When a user starts interacting with your agent, the SessionService creates a
Session object (google.adk.sessions.Session). This object acts as the
container holding everything related to that one specific chat thread. Here
are its key properties:
- Identification (
id,appName,userId): Unique labels for the conversation.id: A unique identifier for this specific conversation thread, essential for retrieving it later. A SessionService object can handle multipleSession(s). This field identifies which particular session object are we referring to. For example, "test_id_modification".app_name: Identifies which agent application this conversation belongs to. For example, "id_modifier_workflow".userId: Links the conversation to a particular user.
- History (
events): A chronological sequence of all interactions (Eventobjects – user messages, agent responses, tool actions) that have occurred within this specific thread. - Session State (
state): A place to store temporary data relevant only to this specific, ongoing conversation. This acts as a scratchpad for the agent during the interaction. We will cover how to use and managestatein detail in the next section. - Activity Tracking (
lastUpdateTime): A timestamp indicating the last time an event occurred in this conversation thread.
Example: Examining session properties
The following code example demonstrates how to list various values stored in a session object:
=== "Python"
```py
from google.adk.sessions import InMemorySessionService, Session
# Create a simple session to examine its properties
temp_service = InMemorySessionService()
example_session = await temp_service.create_session(
app_name="my_app",
user_id="example_user",
state={"initial_key": "initial_value"} # State can be initialized
)
print(f"--- Examining Session Properties ---")
print(f"ID (`id`): {example_session.id}")
print(f"Application Name (`app_name`): {example_session.app_name}")
print(f"User ID (`user_id`): {example_session.user_id}")
print(f"State (`state`): {example_session.state}") # Note: Only shows initial state here
print(f"Events (`events`): {example_session.events}") # Initially empty
print(f"Last Update (`last_update_time`): {example_session.last_update_time:.2f}")
print(f"---------------------------------")
# Clean up (optional for this example)
await temp_service.delete_session(app_name=example_session.app_name,
user_id=example_session.user_id, session_id=example_session.id)
print("The final status of temp_service - ", temp_service)
```
=== "TypeScript"
```typescript
import { InMemorySessionService } from "@google/adk";
// Create a simple session to examine its properties
const tempService = new InMemorySessionService();
const exampleSession = await tempService.createSession({
appName: "my_app",
userId: "example_user",
state: {"initial_key": "initial_value"} // State can be initialized
});
console.log("--- Examining Session Properties ---");
console.log(`ID ('id'): ${exampleSession.id}`);
console.log(`Application Name ('appName'): ${exampleSession.appName}`);
console.log(`User ID ('userId'): ${exampleSession.userId}`);
console.log(`State ('state'): ${JSON.stringify(exampleSession.state)}`); // Note: Only shows initial state here
console.log(`Events ('events'): ${JSON.stringify(exampleSession.events)}`); // Initially empty
console.log(`Last Update ('lastUpdateTime'): ${exampleSession.lastUpdateTime}`);
console.log("---------------------------------");
// Clean up (optional for this example)
const finalStatus = await tempService.deleteSession({
appName: exampleSession.appName,
userId: exampleSession.userId,
sessionId: exampleSession.id
});
console.log("The final status of temp_service - ", finalStatus);
```
=== "Go"
```go
--8<-- "examples/go/snippets/sessions/session_management_example/session_management_example.go:examine_session"
```
=== "Java"
```java
import com.google.adk.sessions.InMemorySessionService;
import com.google.adk.sessions.Session;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
String sessionId = "123";
String appName = "example-app"; // Example app name
String userId = "example-user"; // Example user id
ConcurrentMap<String, Object> initialState = new ConcurrentHashMap<>(Map.of("newKey", "newValue"));
InMemorySessionService exampleSessionService = new InMemorySessionService();
// Create Session
Session exampleSession = exampleSessionService.createSession(
appName, userId, initialState, Optional.of(sessionId)).blockingGet();
System.out.println("Session created successfully.");
System.out.println("--- Examining Session Properties ---");
System.out.printf("ID (`id`): %s%n", exampleSession.id());
System.out.printf("Application Name (`appName`): %s%n", exampleSession.appName());
System.out.printf("User ID (`userId`): %s%n", exampleSession.userId());
System.out.printf("State (`state`): %s%n", exampleSession.state());
System.out.println("------------------------------------");
// Clean up (optional for this example)
var unused = exampleSessionService.deleteSession(appName, userId, sessionId);
```
=== "Kotlin"
```kotlin
import com.google.adk.kt.sessions.InMemorySessionService
import com.google.adk.kt.sessions.SessionKey
val sessionId = "123"
val appName = "example-app"
val userId = "example-user"
val initialState = mapOf("newKey" to "newValue")
val sessionService = InMemorySessionService()
// Create Session
val exampleSession = sessionService.createSession(
key = SessionKey(appName, userId, sessionId),
state = initialState
)
println("Session created successfully.")
println("--- Examining Session Properties ---")
println("ID (`id`): ${exampleSession.key.id}")
println("Application Name (`appName`): ${exampleSession.key.appName}")
println("User ID (`userId`): ${exampleSession.key.userId}")
println("State (`state`): ${exampleSession.state}")
println("------------------------------------")
// Clean up (optional for this example)
sessionService.deleteSession(exampleSession.key)
```
(Note: The state shown above is only the initial state. State updates happen via events, as discussed in the State section.)
Session lifecycle
Here’s a simplified flow of how Session and SessionService work together
during a conversation turn:
- Start or Resume: Your application needs to use the
SessionServiceto eithercreate_session(for a new chat) or use an existing session id. - Context Provided: The
Runnergets the appropriateSessionobject from the appropriate service method, providing the agent with access to the corresponding Session'sstateandevents. - Agent Processing: The user prompts the agent with a query. The agent
analyzes the query and potentially the session
stateandeventshistory to determine the response. - Response & State Update: The agent generates a response (and potentially
flags data to be updated in the
state). TheRunnerpackages this as anEvent. - Save Interaction: The
RunnercallssessionService.append_event(session, event)with thesessionand the neweventas the arguments. The service adds theEventto the history and updates the session'sstatein storage based on information within the event. The session'slast_update_timealso get updated. - Ready for Next: The agent's response goes to the user. The updated
Sessionis now stored by theSessionService, ready for the next turn (which restarts the cycle at step 1, usually with the continuation of the conversation in the current session). - End Conversation: When the conversation is over, your application calls
sessionService.delete_session(...)to clean up the stored session data if it is no longer required.
This cycle highlights how the SessionService ensures conversational continuity
by managing the history and state associated with each Session object.
Managing sessions with a SessionService
As seen above, you don't typically create or manage Session objects directly.
Instead, you use a SessionService. This service acts as the central
manager responsible for the entire lifecycle of your conversation sessions.
Its core responsibilities include:
- Starting New Conversations: Creating fresh
Sessionobjects when a user begins an interaction. - Resuming Existing Conversations: Retrieving a specific
Session(using its ID) so the agent can continue where it left off. - Saving Progress: Appending new interactions (
Eventobjects) to a session's history. This is also the mechanism through which sessionstategets updated (more in theStatesection). - Listing Conversations: Finding the active session threads for a particular user and application.
- Cleaning Up: Deleting
Sessionobjects and their associated data when conversations are finished or no longer needed.
SessionService implementations
ADK provides different SessionService implementations, allowing you to choose
the storage backend that best suits your needs:
InMemorySessionService
- How it works: Stores all session data directly in the application's memory.
- Persistence: None. All conversation data is lost if the application restarts.
- Requires: Nothing extra.
- Best for: Quick development, local testing, examples, and scenarios where long-term persistence isn't required.
=== "Python"
```py
from google.adk.sessions import InMemorySessionService
session_service = InMemorySessionService()
```
=== "TypeScript"
```typescript
import { InMemorySessionService } from "@google/adk";
const sessionService = new InMemorySessionService();
```
=== "Go"
```go
import "google.golang.org/adk/v2/session"
inMemoryService := session.InMemoryService()
```
=== "Java"
```java
import com.google.adk.sessions.InMemorySessionService;
InMemorySessionService exampleSessionService = new InMemorySessionService();
```
=== "Kotlin"
```kotlin
import com.google.adk.kt.sessions.InMemorySessionService
val sessionService = InMemorySessionService()
```
VertexAiSessionService
- How it works: Uses Google Cloud Agent Platform infrastructure via API calls for session management.
- Persistence: Yes. Data is managed reliably and scalably via Agent Runtime.
- Requires:
- A Google Cloud project.
- The
gcpextra, installed withpip install google-adk[gcp]. - A Google Cloud storage bucket that can be configured by this step.
- An Agent Runtime resource name/ID that can setup following this tutorial.
- If you do not have a Google Cloud project and you want to try the VertexAiSessionService, see Agent Platform Express Mode.
- Best for: Scalable production applications deployed on Google Cloud, especially when integrating with other Agent Platform features.
=== "Python"
```py
# Requires: pip install google-adk[gcp]
# Plus GCP setup and authentication
from google.adk.sessions import VertexAiSessionService
PROJECT_ID = "your-gcp-project-id"
LOCATION = "us-central1"
# The app_name used with this service should be the Reasoning Engine ID or name
REASONING_ENGINE_APP_NAME = "projects/your-gcp-project-id/locations/us-central1/reasoningEngines/your-engine-id"
session_service = VertexAiSessionService(project=PROJECT_ID, location=LOCATION)
# Use REASONING_ENGINE_APP_NAME when calling service methods, e.g.:
# session = await session_service.create_session(app_name=REASONING_ENGINE_APP_NAME, ...)
```
=== "Go"
```go
import "google.golang.org/adk/v2/session"
// 2. VertexAIService
// Before running, ensure your environment is authenticated:
// gcloud auth application-default login
// export GOOGLE_CLOUD_PROJECT="your-gcp-project-id"
// export GOOGLE_CLOUD_LOCATION="your-gcp-location"
modelName := "gemini-flash-latest" // Replace with your desired model
vertexService, err := session.VertexAIService(ctx, modelName)
if err != nil {
log.Printf("Could not initialize VertexAIService (this is expected if the gcloud project is not set): %v", err)
} else {
fmt.Println("Successfully initialized VertexAIService.")
}
```
=== "Java"
```java
// Please look at the set of requirements above, consequently export the following in your bashrc file:
// export GOOGLE_CLOUD_PROJECT=my_gcp_project
// export GOOGLE_CLOUD_LOCATION=us-central1
// export GOOGLE_API_KEY=my_api_key
import com.google.adk.sessions.VertexAiSessionService;
import java.util.UUID;
String sessionId = UUID.randomUUID().toString();
String reasoningEngineAppName = "123456789";
String userId = "u_123"; // Example user id
ConcurrentMap<String, Object> initialState = new
ConcurrentHashMap<>(); // No initial state needed for this example
VertexAiSessionService sessionService = new VertexAiSessionService();
Session mySession =
sessionService
.createSession(reasoningEngineAppName, userId, initialState, Optional.of(sessionId))
.blockingGet();
```
=== "Kotlin"
`VertexAiSessionService` is JVM-only in ADK Kotlin. It is not available on
Android; use it from a server-side agent.
```kotlin
import com.google.adk.kt.sessions.SessionKey
import com.google.adk.kt.sessions.VertexAiSessionService
import kotlinx.coroutines.runBlocking
// The reasoning engine is pinned here, at construction. In the other tabs
// the engine is chosen per call, through `app_name`; in Kotlin `appName`
// is never parsed for it and is only a label on the session.
val sessionService =
VertexAiSessionService(
project = "your-gcp-project-id",
location = "us-central1",
// The bare numeric engine id. A full
// "projects/.../reasoningEngines/..." resource name is rejected;
// project and location are separate arguments.
reasoningEngineId = "1234567890",
)
// Session methods are suspend functions; `runBlocking` here is the
// counterpart of the Java tab's `.blockingGet()`.
val mySession = runBlocking {
// A null id lets the service assign one.
sessionService.createSession(SessionKey("example-app", "u_123", id = null))
}
```
For more information on connecting to Google Cloud from ADK agents, see Connect to Google Cloud and Agent Platform.
DatabaseSessionService
- How it works: Connects to a relational database (e.g., PostgreSQL, MySQL, SQLite) to store session data persistently in tables.
- Persistence: Yes. Data survives application restarts.
- Requires: A configured database and the
dbextra, installed withpip install google-adk[db]. - Best for: Applications needing reliable, persistent storage that you manage yourself.
from google.adk.sessions import DatabaseSessionService
# Example using a local SQLite file:
# Note: The implementation requires an async database driver.
# For SQLite, use 'sqlite+aiosqlite' instead of 'sqlite' to ensure async compatibility.
db_url = "sqlite+aiosqlite:///./my_agent_data.db"
session_service = DatabaseSessionService(db_url=db_url)
Concurrency and locking
The DatabaseSessionService ensures data integrity during concurrent operations
through a two-tiered locking architecture:
- In-Process locking: The service uses an internal, in-process lock to
serialize
append_eventcalls for the same session. This prevents race conditions when multiple requests try to update the same session simultaneously within the same process. - Row-Level locking: For PostgreSQL, MySQL, and MariaDB, the service uses
row-level locking (via
SELECT ... FOR UPDATE) to prevent race conditions when multiple processes or replicas try to update the same session simultaneously.
!!! warning "Async driver requirement"
`DatabaseSessionService` requires an async database driver. When using
SQLite, you must use `sqlite+aiosqlite` instead of `sqlite` in your
connection string. For other databases (PostgreSQL, MySQL), ensure you're
using an async-compatible driver, such as `asyncpg` for PostgreSQL,
`aiomysql` for MySQL.
!!! note "Session database schema change in ADK Python v1.22.0"
The schema for the session database changed in ADK Python v1.22.0, which
requires migration of the Session Database. For more information, see
[Session database schema migration](/sessions/session/migrate/).
Troubleshoot session errors
During execution, ADK can raise specific exceptions to help you identify configuration or state issues.
SessionNotFoundError
Raised when a runner attempts to access or execute a session that does not exist
in the active session store. Inherits from ValueError for backward
compatibility.
- Common causes: an invalid, expired, or missing
session_id; running a session before it has been created. - How to resolve: ensure the session exists first via
create_session(...), or construct theRunnerwithauto_create_session=True.