adk-session
Session management and state persistence for Rust Agent Development Kit (ADK-Rust) agents.
Overview
adk-session provides session and state management for the Rust Agent Development Kit (ADK-Rust):
- InMemorySessionService - Simple in-memory session storage
- SqliteSessionService - SQLite-backed persistence (
sqlitefeature) - PostgresSessionService - PostgreSQL-backed persistence (
postgresfeature) - RedisSessionService - Redis-backed persistence (
redisfeature) - MongoSessionService - MongoDB-backed persistence (
mongodbfeature) - Neo4jSessionService - Neo4j-backed persistence (
neo4jfeature) - FirestoreSessionService - Firestore-backed persistence (
firestorefeature) - VertexAiSessionService - Vertex AI Session API backend (
vertex-sessionfeature) - Schema Migrations - Versioned, forward-only migrations for all database backends
Installation
[]
= "2.0.0"
Or use the meta-crate:
[]
= { = "2.0.0", = ["sessions"] }
Quick Start
use ;
use json;
use HashMap;
let service = new;
let mut initial_state = new;
initial_state.insert;
let session = service.create.await?;
let name = session.state.get;
State Prefixes
| Prefix | Purpose | Persistence |
|---|---|---|
user: |
User preferences | Across sessions |
app: |
Application state | Application-wide |
temp: |
Temporary data | Current turn only |
Feature Flags
| Feature | Backend | Description |
|---|---|---|
sqlite |
SQLite | Single-node persistence via sqlx |
database |
SQLite | Alias for sqlite (backward compat) |
postgres |
PostgreSQL | Production-grade relational persistence |
redis |
Redis | Low-latency in-memory persistence via fred |
mongodb |
MongoDB | Document-oriented persistence |
neo4j |
Neo4j | Graph database persistence |
firestore |
Firestore | Google Cloud Firestore persistence |
vertex-session |
Vertex AI | Vertex AI Session API backend |
encrypted-session |
AES-256-GCM | Transparent encryption at rest with key rotation |
# SQLite
= { = "2.0.0", = ["sqlite"] }
# PostgreSQL
= { = "2.0.0", = ["postgres"] }
# Redis
= { = "2.0.0", = ["redis"] }
# Encrypted sessions
= { = "2.0.0", = ["encrypted-session"] }
# Vertex AI sessions through the umbrella crate
= { = "2.0.0", = ["vertex-session"] }
Vertex AI Sessions
VertexAiSessionService stores sessions through the GA v1 Vertex AI Agent
Engine Session API:
use ;
let config = new
.with_reasoning_engine;
let service = new_with_adc?;
Caller-facing session IDs remain unchanged. The backend derives a deterministic
remote ID from the complete (app_name, user_id, session_id) identity and stores
a protected identity marker in Vertex session state. The marker is removed from
all returned state and cannot be supplied through create state or event state
deltas. This permits the same logical session ID to exist safely across apps and
users, including when several apps share one reasoning engine.
The default endpoint depends on the configured location:
| Location | Endpoint |
|---|---|
global |
https://aiplatform.googleapis.com |
us |
https://aiplatform.us.rep.googleapis.com |
eu |
https://aiplatform.eu.rep.googleapis.com |
Region such as us-central1 |
https://us-central1-aiplatform.googleapis.com |
Production boundaries
| Boundary | Behavior |
|---|---|
| Vertex user ID | At most 128 Unicode scalar values |
| Encoded request body | 64 MiB per create/append request |
| Decoded response body | 64 MiB per response and in aggregate for one paginated list operation |
| Pagination | 120-second total deadline; recent-event bounds and timestamp filters are pushed into the API |
| Page token | At most 64 KiB |
| Transport | 10-second connect, 30-second credential-header, and 120-second HTTP request deadlines |
| Long-running operations | 120-second local polling deadline |
| JSON and Struct values | At most 64 nested levels |
Use with_max_request_bytes(), with_max_response_bytes(), and
with_pagination_timeout() to select deployment-specific bounds. Raising a
body limit weakens the default allocation protection; neither body-byte budget
is a total process-memory ceiling.
Custom endpoints are security-sensitive: the origin receives Google authorization headers and complete session/event payloads. The caller must configure a trusted endpoint. The constructor accepts an HTTPS origin shape without userinfo, a path, query, or fragment; loopback HTTP is available for tests. Redirects are disabled.
Create, delete, and append HTTP 408/5xx, transport/body, malformed 2xx,
or invalid successful-result failures can occur after the service commits the
mutation. They return
session.vertex.{create,delete,append}_outcome_ambiguous with
retry.should_retry = false. After create or delete returns an operation name,
poll transport/status failures and malformed or invalid successful responses
use the same non-retryable ambiguity contract and include the operation name.
Inspect the target session, operation, or event list before any manual retry.
A terminal done: true operation error is a known failed result instead: it
retains session.vertex.operation_failed and its category-derived retry hint.
Direct HTTP 4xx rejections other than 408 remain definitive; 429 is
retryable. Any poll failure after an operation is accepted, including 429, is
ambiguous and non-retryable. The backend does not throttle or retry internally.
Size capacity using the current Vertex AI
quotas.
Run the ignored GA v1 canary only against an existing reasoning engine with
Application Default Credentials configured. It creates and deletes one session
and one representative event; it never creates or deletes the engine.
ADK_VERTEX_LIVE_TEST=1 \
GOOGLE_CLOUD_PROJECT=PROJECT_ID \
GOOGLE_CLOUD_LOCATION=LOCATION \
GOOGLE_CLOUD_REASONING_ENGINE_ID=REASONING_ENGINE_ID \
Arbitrary Google rawEvent Struct values remain opaque. Reappend preserves
every original key and value and adds the reserved _adkRust envelope; removing
that envelope yields the original Struct. A pre-existing malformed _adkRust
key fails closed. Google ADK projection is best-effort even when a Struct has a
non-empty string id, numeric timestamp, and string invocationId and
author; incompatible content, actions, metadata, or optional fields do not
reject the canonical SessionEvent.
The GA v1 FunctionCall and FunctionResponse wire messages have no id.
ID-bearing ADK function parts and empty or noncanonical Base64 thought-signature
bytes use the lossless private rawEvent path. Part.mediaResolution accepts a
bounded JSON object on any otherwise-valid canonical part. Top-level
inlineData/fileData displayName values are accepted from the deployed GA
wire. ADK projection omits these provider-only fields, while the canonical
sidecar preserves them across reappend. Private-envelope validation treats
omitted proto3 default scalar fields as equivalent to their empty private
values, accepts safe integer/float normalization inside Vertex Struct values,
and restores exact private scalar presence.
Legacy unmarked sessions
When reasoning_engine is omitted, app_name must be the canonical nonzero
numeric reasoning-engine ID without leading zeros. That ID selects the isolated
parent, and the backend can access unmarked Python ADK or pre-v2 sessions by
their direct remote ID. Logical app names and full resource names require
with_reasoning_engine(). When a fixed reasoning engine is shared, unmarked
access is disabled by default. Enable it for exactly one audited app during
migration:
use ;
let legacy_config = new
.with_reasoning_engine;
let service = new_with_adc?
.allow_unmarked_sessions_for_app;
Important: Unmarked sessions carry no app marker. Only enable this compatibility mode when the selected app exclusively owns the legacy sessions in that reasoning engine. The backend still requires the exact
user_idand rejects marked-direct, reserved-ID, and computed/direct ambiguities.
Use append_event_for_identity() for new code. The legacy
append_event(session_id, event) method works only after a create, get, or list
operation has cached exactly one app/user scope for that ID. The cache is
bounded; long-running processes may need to get or list an older session again
after its scope is evicted.
Encrypted Sessions
Wrap any SessionService with EncryptedSession to encrypt session state at rest using AES-256-GCM:
use ;
let key = generate;
let inner = new;
let service = new;
// Use like any SessionService — encryption is transparent
Key rotation is supported by passing previous keys:
let new_key = generate;
let old_key = from_env?;
let service = new;
Schema Migrations
All database backends (SQLite, PostgreSQL, MongoDB, Neo4j) include a versioned migration system. Migrations are forward-only, idempotent, and tracked in a _schema_migrations registry table.
use SqliteSessionService;
let service = new.await?;
// Run all pending migrations
service.migrate.await?;
// Check current schema version
let version = service.schema_version.await?;
println!;
Each backend detects pre-existing tables (baseline detection) and registers them as already applied, so migrate() is safe to call on both fresh and existing databases.
Rename: DatabaseSessionService → SqliteSessionService
As of v0.4.0, DatabaseSessionService was renamed to SqliteSessionService to accurately reflect that it is a SQLite-only backend. The deprecated type alias was removed in v0.7.0. Update your imports:
use SqliteSessionService;
Related Crates
- adk-rust - Meta-crate with all components
- adk-core - Core
Sessiontrait - adk-runner - Uses sessions for execution
License
Apache-2.0
Part of ADK-Rust
This crate is part of the ADK-Rust framework for building AI agents in Rust.