Skip to content

Session Stores

A SessionStore<S> is where a server-managed agent keeps its sessions. Pass one to .store(...) when you define an agent to persist snapshots; omit it to run the agent client-managed (stateless). Several implementations ship out of the box, and you can write your own.

StorePersistenceGood for
InMemorySessionStoreIn-process; lost on restartTests and short-lived processes
FileSessionStoreJSON files on local diskLocal development and single-process deployments
FirestoreSessionStoreGoogle Cloud FirestoreProduction on Google Cloud (Cloud Run, Firebase Functions)
DynamoDbSessionStoreAmazon DynamoDBProduction on AWS
CosmosSessionStoreAzure Cosmos DBProduction on Azure
PostgresSessionStorePostgreSQLProduction on any SQL-first stack
MongoSessionStoreMongoDBProduction on any document-first stack

If you plan to use chat.abort() or the /abort HTTP endpoint, pick a store that supports change notifications: FileSessionStore, FirestoreSessionStore, DynamoDbSessionStore, CosmosSessionStore, PostgresSessionStore, and MongoSessionStore do; InMemorySessionStore does not, so abort() is a no-op there. See Sessions.

Holds snapshots in memory. State is not shared across processes and is discarded on exit.

import com.google.genkit.ai.agent.InMemorySessionStore;
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(new InMemorySessionStore<>())
.build());

Persists each snapshot as a JSON file under a directory you choose.

import com.google.genkit.ai.agent.FileSessionStore;
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(new FileSessionStore<>("./.snapshots"))
.build());

Writes are atomic — a snapshot is written to a temporary file and then renamed — so a process interrupted mid-write never leaves a partial snapshot behind.

Use the builder for finer control:

FileSessionStore<Map<String, Object>> store =
FileSessionStore.<Map<String, Object>>builder("./.snapshots")
.prefix("prod") // subdirectory to group sessions (default "global")
.maxPersistedChainLength(10) // prune older snapshots in a chain; 0 = keep all
.rejectBranchingSessions(true) // fail if a session ends up with more than one branch
.snapshotWatchPollIntervalMs(500) // how often change subscribers are polled (default 2000)
.build();

Snapshots for a session are stored under the prefix subdirectory, with a small pointer file tracking the latest snapshot per session.

FileSessionStore can notify you when a snapshot’s status changes — useful for tracking a background turn:

try (AutoCloseable sub = store.onSnapshotStateChange(
snapshotId,
snap -> System.out.println("Status changed: " + snap.getStatus()),
null)) {
// ... long-running turn ...
}

Stores snapshots in Google Cloud Firestore. It lives in the Firebase plugin, so add the dependency:

<dependency>
<groupId>com.google.genkit</groupId>
<artifactId>genkit-plugin-firebase</artifactId>
<version>${genkit.version}</version>
</dependency>
import com.google.genkit.plugins.firebase.session.FirestoreSessionStore;
import com.google.cloud.firestore.Firestore;
import com.google.firebase.cloud.FirestoreClient;
Firestore db = FirestoreClient.getFirestore();
FirestoreSessionStore<Map<String, Object>> store =
new FirestoreSessionStore<>(db); // uses the default "genkit-sessions" collection
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(store)
.build());

To customize the collection name or checkpointing behavior, pass FirestoreSessionStoreOptions as the second constructor argument. See the Firebase plugin for details.

Stores snapshots in Amazon DynamoDB. It lives in the AWS Bedrock plugin, so add the dependency:

<dependency>
<groupId>com.google.genkit</groupId>
<artifactId>genkit-plugin-aws-bedrock</artifactId>
<version>${genkit.version}</version>
</dependency>
import com.google.genkit.plugins.awsbedrock.session.DynamoDbSessionStore;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
DynamoDbClient db = DynamoDbClient.create(); // uses the default AWS credential chain and region
DynamoDbSessionStore<Map<String, Object>> store =
new DynamoDbSessionStore<>(db); // uses the default "genkit-sessions" table
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(store)
.build());

All records live in a single table with a partition key pk and sort key sk (both strings). Create the table ahead of time, or let the store create it on first use:

DynamoDbSessionStore<Map<String, Object>> store =
new DynamoDbSessionStore<>(
db,
DynamoDbSessionStoreOptions.builder()
.tableName("genkit-sessions") // table name (default "genkit-sessions")
.createTableIfNotExists(true) // auto-create the table on first use (default false)
.pollIntervalMs(500) // how often change subscribers are polled (default 2000)
.build());

The default shard size is 350 KiB, kept under DynamoDB’s 400 KB item-size limit. DynamoDbSessionStore supports onSnapshotStateChange (via polling), so chat.abort() works.

Stores snapshots in Azure Cosmos DB. It lives in the Azure AI Foundry plugin, so add the dependency:

<dependency>
<groupId>com.google.genkit</groupId>
<artifactId>genkit-plugin-azure-foundry</artifactId>
<version>${genkit.version}</version>
</dependency>
import com.google.genkit.plugins.azurefoundry.session.CosmosSessionStore;
import com.azure.cosmos.CosmosClient;
import com.azure.cosmos.CosmosClientBuilder;
CosmosClient client = new CosmosClientBuilder()
.endpoint(System.getenv("COSMOS_ENDPOINT"))
.key(System.getenv("COSMOS_KEY"))
.buildClient();
CosmosSessionStore<Map<String, Object>> store =
new CosmosSessionStore<>(client); // uses database "genkit", container "genkit-sessions"
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(store)
.build());

All records live in a single container partitioned by /pk. Create the database and container ahead of time, or let the store create them on first use:

CosmosSessionStore<Map<String, Object>> store =
new CosmosSessionStore<>(
client,
CosmosSessionStoreOptions.builder()
.databaseName("genkit") // database name (default "genkit")
.containerName("genkit-sessions") // container name (default "genkit-sessions")
.createIfNotExists(true) // auto-create database + container (default false)
.pollIntervalMs(500) // how often change subscribers are polled (default 2000)
.build());

The default shard size is 1 MiB, kept under Cosmos DB’s 2 MB document-size limit. CosmosSessionStore supports onSnapshotStateChange (via polling), so chat.abort() works.

Stores snapshots in PostgreSQL. It lives in the PostgreSQL plugin, so add the dependency:

<dependency>
<groupId>com.google.genkit</groupId>
<artifactId>genkit-plugin-postgresql</artifactId>
<version>${genkit.version}</version>
</dependency>
import com.google.genkit.plugins.postgresql.session.PostgresSessionStore;
import com.google.genkit.plugins.postgresql.session.PostgresSessionStoreOptions;
import org.postgresql.ds.PGSimpleDataSource;
PGSimpleDataSource dataSource = new PGSimpleDataSource();
dataSource.setUrl("jdbc:postgresql://localhost:5432/genkit");
dataSource.setUser("postgres");
dataSource.setPassword("postgres");
PostgresSessionStore<Map<String, Object>> store =
new PostgresSessionStore<>(dataSource); // uses the default "genkit_sessions" table
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(store)
.build());

All records live in a single table keyed by (pk, id) with a JSONB doc payload and a version column for optimistic concurrency. Create the table ahead of time, or let the store create it on first use:

PostgresSessionStore<Map<String, Object>> store =
new PostgresSessionStore<>(
dataSource,
PostgresSessionStoreOptions.builder()
.tableName("genkit_sessions") // table name (default "genkit_sessions")
.createTableIfNotExists(true) // auto-create the table on first use (default false)
.pollIntervalMs(500) // how often change subscribers are polled (default 2000)
.build());

The default shard size is 1 MiB. PostgresSessionStore supports onSnapshotStateChange (via polling), so chat.abort() works.

Stores snapshots in MongoDB. It lives in the MongoDB plugin, so add the dependency:

<dependency>
<groupId>com.google.genkit</groupId>
<artifactId>genkit-plugin-mongodb</artifactId>
<version>${genkit.version}</version>
</dependency>
import com.google.genkit.plugins.mongodb.session.MongoSessionStore;
import com.google.genkit.plugins.mongodb.session.MongoSessionStoreOptions;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
MongoClient client = MongoClients.create("mongodb://localhost:27017");
MongoSessionStore<Map<String, Object>> store =
new MongoSessionStore<>(client); // uses database "genkit", collection "genkit_sessions"
Agent<Map<String, Object>> agent = genkit.beta().defineAgent(
AgentConfig.<Map<String, Object>>builder()
.name("myAgent")
.system("You are a helpful assistant.")
.store(store)
.build());

All records live in a single collection whose _id is <prefix>::<recordId>; each document carries a version field for optimistic concurrency. The database and collection are created automatically on first write. Tune it with MongoSessionStoreOptions:

MongoSessionStore<Map<String, Object>> store =
new MongoSessionStore<>(
client,
MongoSessionStoreOptions.builder()
.databaseName("genkit") // database name (default "genkit")
.collectionName("genkit_sessions") // collection name (default "genkit_sessions")
.pollIntervalMs(500) // how often change subscribers are polled (default 2000)
.build());

The default shard size is 1 MiB, kept under MongoDB’s 16 MB document-size limit. MongoSessionStore supports onSnapshotStateChange (via polling), so chat.abort() works.

Implement SessionStore<S> to use any backend — a database, a cache, a cloud object store:

import com.google.genkit.ai.agent.SessionStore;
import com.google.genkit.ai.agent.SessionSnapshot;
import com.google.genkit.ai.agent.GetSnapshotOptions;
import com.google.genkit.ai.agent.SessionStoreOptions;
import com.google.genkit.ai.agent.SnapshotMutator;
public class RedisSessionStore<S> implements SessionStore<S> {
@Override
public SessionSnapshot<S> getSnapshot(GetSnapshotOptions opts) {
// Read by snapshotId or sessionId.
...
}
@Override
public String saveSnapshot(String snapshotId, SnapshotMutator<S> mutator,
SessionStoreOptions options) {
// Apply the mutator to the existing snapshot, persist the result,
// and return the final snapshotId.
...
}
}