Skip to content

Commit d899f6f

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Adding a SessionKey for typeSafety
PiperOrigin-RevId: 877372854
1 parent 6a2669b commit d899f6f

6 files changed

Lines changed: 330 additions & 13 deletions

File tree

core/src/main/java/com/google/adk/artifacts/BaseArtifactService.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.adk.artifacts;
1818

19+
import com.google.adk.sessions.SessionKey;
1920
import com.google.common.collect.ImmutableList;
2021
import com.google.genai.types.Part;
2122
import io.reactivex.rxjava3.core.Completable;
@@ -39,6 +40,12 @@ public interface BaseArtifactService {
3940
Single<Integer> saveArtifact(
4041
String appName, String userId, String sessionId, String filename, Part artifact);
4142

43+
/** Saves an artifact. */
44+
default Single<Integer> saveArtifact(SessionKey sessionKey, String filename, Part artifact) {
45+
return saveArtifact(
46+
sessionKey.appName(), sessionKey.userId(), sessionKey.id(), filename, artifact);
47+
}
48+
4249
/**
4350
* Saves an artifact and returns it with fileData if available.
4451
*
@@ -58,18 +65,35 @@ default Single<Part> saveAndReloadArtifact(
5865
.flatMap(version -> loadArtifact(appName, userId, sessionId, filename, version).toSingle());
5966
}
6067

68+
/** Saves an artifact and returns it with fileData if available. */
69+
default Single<Part> saveAndReloadArtifact(
70+
SessionKey sessionKey, String filename, Part artifact) {
71+
return saveAndReloadArtifact(
72+
sessionKey.appName(), sessionKey.userId(), sessionKey.id(), filename, artifact);
73+
}
74+
6175
/** Loads the latest version of an artifact from the service. */
6276
default Maybe<Part> loadArtifact(
6377
String appName, String userId, String sessionId, String filename) {
6478
return loadArtifact(appName, userId, sessionId, filename, Optional.empty());
6579
}
6680

81+
/** Loads the latest version of an artifact from the service. */
82+
default Maybe<Part> loadArtifact(SessionKey sessionKey, String filename) {
83+
return loadArtifact(sessionKey.appName(), sessionKey.userId(), sessionKey.id(), filename);
84+
}
85+
6786
/** Loads a specific version of an artifact from the service. */
6887
default Maybe<Part> loadArtifact(
6988
String appName, String userId, String sessionId, String filename, int version) {
7089
return loadArtifact(appName, userId, sessionId, filename, Optional.of(version));
7190
}
7291

92+
default Maybe<Part> loadArtifact(SessionKey sessionKey, String filename, int version) {
93+
return loadArtifact(
94+
sessionKey.appName(), sessionKey.userId(), sessionKey.id(), filename, version);
95+
}
96+
7397
/**
7498
* @deprecated Use {@link #loadArtifact(String, String, String, String)} or {@link
7599
* #loadArtifact(String, String, String, String, int)} instead.
@@ -88,6 +112,10 @@ Maybe<Part> loadArtifact(
88112
*/
89113
Single<ListArtifactsResponse> listArtifactKeys(String appName, String userId, String sessionId);
90114

115+
default Single<ListArtifactsResponse> listArtifactKeys(SessionKey sessionKey) {
116+
return listArtifactKeys(sessionKey.appName(), sessionKey.userId(), sessionKey.id());
117+
}
118+
91119
/**
92120
* Deletes an artifact.
93121
*
@@ -98,6 +126,10 @@ Maybe<Part> loadArtifact(
98126
*/
99127
Completable deleteArtifact(String appName, String userId, String sessionId, String filename);
100128

129+
default Completable deleteArtifact(SessionKey sessionKey, String filename) {
130+
return deleteArtifact(sessionKey.appName(), sessionKey.userId(), sessionKey.id(), filename);
131+
}
132+
101133
/**
102134
* Lists all the versions (as revision IDs) of an artifact.
103135
*
@@ -109,4 +141,8 @@ Maybe<Part> loadArtifact(
109141
*/
110142
Single<ImmutableList<Integer>> listVersions(
111143
String appName, String userId, String sessionId, String filename);
144+
145+
default Single<ImmutableList<Integer>> listVersions(SessionKey sessionKey, String filename) {
146+
return listVersions(sessionKey.appName(), sessionKey.userId(), sessionKey.id(), filename);
147+
}
112148
}

core/src/main/java/com/google/adk/runner/Runner.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.adk.sessions.BaseSessionService;
3636
import com.google.adk.sessions.InMemorySessionService;
3737
import com.google.adk.sessions.Session;
38+
import com.google.adk.sessions.SessionKey;
3839
import com.google.adk.summarizer.EventsCompactionConfig;
3940
import com.google.adk.summarizer.LlmEventSummarizer;
4041
import com.google.adk.summarizer.SlidingWindowEventCompactor;
@@ -383,6 +384,25 @@ public Flowable<Event> runAsync(
383384
.flatMapPublisher(session -> this.runAsyncImpl(session, newMessage, runConfig, stateDelta));
384385
}
385386

387+
/** See {@link #runAsync(String, String, Content, RunConfig, Map)}. */
388+
public Flowable<Event> runAsync(
389+
SessionKey sessionKey,
390+
Content newMessage,
391+
RunConfig runConfig,
392+
@Nullable Map<String, Object> stateDelta) {
393+
return runAsync(sessionKey.userId(), sessionKey.id(), newMessage, runConfig, stateDelta);
394+
}
395+
396+
/** See {@link #runAsync(String, String, Content, RunConfig, Map)}. */
397+
public Flowable<Event> runAsync(SessionKey sessionKey, Content newMessage, RunConfig runConfig) {
398+
return runAsync(sessionKey, newMessage, runConfig, /* stateDelta= */ null);
399+
}
400+
401+
/** See {@link #runAsync(String, String, Content, RunConfig, Map)}. */
402+
public Flowable<Event> runAsync(SessionKey sessionKey, Content newMessage) {
403+
return runAsync(sessionKey, newMessage, RunConfig.builder().build());
404+
}
405+
386406
/** See {@link #runAsync(String, String, Content, RunConfig, Map)}. */
387407
public Flowable<Event> runAsync(String userId, String sessionId, Content newMessage) {
388408
return runAsync(userId, sessionId, newMessage, RunConfig.builder().build());
@@ -671,6 +691,17 @@ public Flowable<Event> runLive(
671691
.flatMapPublisher(session -> this.runLive(session, liveRequestQueue, runConfig));
672692
}
673693

694+
/**
695+
* Retrieves the session and runs the agent in live mode.
696+
*
697+
* @return stream of events from the agent.
698+
* @throws IllegalArgumentException if the session is not found.
699+
*/
700+
public Flowable<Event> runLive(
701+
SessionKey sessionKey, LiveRequestQueue liveRequestQueue, RunConfig runConfig) {
702+
return runLive(sessionKey.userId(), sessionKey.id(), liveRequestQueue, runConfig);
703+
}
704+
674705
/**
675706
* Runs the agent asynchronously with a default user ID.
676707
*

core/src/main/java/com/google/adk/sessions/BaseSessionService.java

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ default Single<Session> createSession(
7878
return createSession(appName, userId, ensureConcurrentMap(state), sessionId);
7979
}
8080

81+
/**
82+
* Creates a new session with the specified parameters.
83+
*
84+
* @param sessionKey The session key containing appName, userId and sessionId.
85+
* @param state An optional map representing the initial state of the session. Can be null or
86+
* empty.
87+
*/
88+
default Single<Session> createSession(
89+
SessionKey sessionKey, @Nullable Map<String, Object> state) {
90+
return createSession(sessionKey.appName(), sessionKey.userId(), state, sessionKey.id());
91+
}
92+
8193
/**
8294
* Creates a new session with the specified application name and user ID, using a default state
8395
* (null) and allowing the service to generate a unique session ID.
@@ -94,6 +106,14 @@ default Single<Session> createSession(String appName, String userId) {
94106
return createSession(appName, userId, null, null);
95107
}
96108

109+
/**
110+
* Creates a new session with the specified application name and user ID, using a default state
111+
* (null) and allowing the service to generate a unique session ID.
112+
*/
113+
default Single<Session> createSession(SessionKey sessionKey) {
114+
return createSession(sessionKey.appName(), sessionKey.userId(), null, sessionKey.id());
115+
}
116+
97117
/**
98118
* Retrieves a specific session, optionally filtering the events included.
99119
*
@@ -110,6 +130,12 @@ default Single<Session> createSession(String appName, String userId) {
110130
Maybe<Session> getSession(
111131
String appName, String userId, String sessionId, Optional<GetSessionConfig> config);
112132

133+
/** Retrieves a specific session, optionally filtering the events included. */
134+
default Maybe<Session> getSession(SessionKey sessionKey, @Nullable GetSessionConfig config) {
135+
return getSession(
136+
sessionKey.appName(), sessionKey.userId(), sessionKey.id(), Optional.ofNullable(config));
137+
}
138+
113139
/**
114140
* Lists sessions associated with a specific application and user.
115141
*
@@ -123,6 +149,11 @@ Maybe<Session> getSession(
123149
*/
124150
Single<ListSessionsResponse> listSessions(String appName, String userId);
125151

152+
/** Lists sessions associated with a specific application and user. */
153+
default Single<ListSessionsResponse> listSessions(SessionKey sessionKey) {
154+
return listSessions(sessionKey.appName(), sessionKey.userId());
155+
}
156+
126157
/**
127158
* Deletes a specific session.
128159
*
@@ -134,6 +165,11 @@ Maybe<Session> getSession(
134165
*/
135166
Completable deleteSession(String appName, String userId, String sessionId);
136167

168+
/** Deletes a specific session. */
169+
default Completable deleteSession(SessionKey sessionKey) {
170+
return deleteSession(sessionKey.appName(), sessionKey.userId(), sessionKey.id());
171+
}
172+
137173
/**
138174
* Lists the events within a specific session. Supports pagination via the response object.
139175
*
@@ -147,6 +183,11 @@ Maybe<Session> getSession(
147183
*/
148184
Single<ListEventsResponse> listEvents(String appName, String userId, String sessionId);
149185

186+
/** Lists the events within a specific session. */
187+
default Single<ListEventsResponse> listEvents(SessionKey sessionKey) {
188+
return listEvents(sessionKey.appName(), sessionKey.userId(), sessionKey.id());
189+
}
190+
150191
/**
151192
* Closes a session. This is currently a placeholder and may involve finalizing session state or
152193
* performing cleanup actions in future implementations. The default implementation does nothing.
@@ -190,20 +231,18 @@ default Single<Event> appendEvent(Session session, Event event) {
190231
EventActions actions = event.actions();
191232
if (actions != null) {
192233
Map<String, Object> stateDelta = actions.stateDelta();
193-
if (stateDelta != null && !stateDelta.isEmpty()) {
194-
Map<String, Object> sessionState = session.state();
195-
if (sessionState != null) {
196-
stateDelta.forEach(
197-
(key, value) -> {
198-
if (!key.startsWith(State.TEMP_PREFIX)) {
199-
if (value == State.REMOVED) {
200-
sessionState.remove(key);
201-
} else {
202-
sessionState.put(key, value);
203-
}
234+
Map<String, Object> sessionState = session.state();
235+
if (stateDelta != null && !stateDelta.isEmpty() && sessionState != null) {
236+
stateDelta.forEach(
237+
(key, value) -> {
238+
if (!key.startsWith(State.TEMP_PREFIX)) {
239+
if (value == State.REMOVED) {
240+
sessionState.remove(key);
241+
} else {
242+
sessionState.put(key, value);
204243
}
205-
});
206-
}
244+
}
245+
});
207246
}
208247
}
209248

core/src/main/java/com/google/adk/sessions/Session.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ public static Builder builder(String id) {
4949
return new Builder(id);
5050
}
5151

52+
/** Creates a new {@link Builder} with the given session key. */
53+
public static Builder builder(SessionKey sessionKey) {
54+
return new Builder(sessionKey);
55+
}
56+
5257
/** Builder for {@link Session}. */
5358
public static final class Builder {
5459
private String id;
@@ -62,6 +67,13 @@ public Builder(String id) {
6267
this.id = id;
6368
}
6469

70+
/** Creates a new {@link Builder} with the given session key. */
71+
public Builder(SessionKey sessionKey) {
72+
this.id = sessionKey.id();
73+
this.appName = sessionKey.appName();
74+
this.userId = sessionKey.userId();
75+
}
76+
6577
@JsonCreator
6678
private Builder() {}
6779

@@ -72,6 +84,15 @@ public Builder id(String id) {
7284
return this;
7385
}
7486

87+
/** Sets the session key. */
88+
@CanIgnoreReturnValue
89+
public Builder sessionKey(SessionKey sessionKey) {
90+
this.id = sessionKey.id();
91+
this.appName = sessionKey.appName();
92+
this.userId = sessionKey.userId();
93+
return this;
94+
}
95+
7596
@CanIgnoreReturnValue
7697
public Builder state(State state) {
7798
this.state = state;
@@ -130,6 +151,11 @@ public Session build() {
130151
}
131152
}
132153

154+
/** Returns the session key. */
155+
public SessionKey sessionKey() {
156+
return new SessionKey(appName, userId, id);
157+
}
158+
133159
@JsonProperty("id")
134160
public String id() {
135161
return id;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.adk.sessions;
18+
19+
import com.fasterxml.jackson.annotation.JsonCreator;
20+
import com.fasterxml.jackson.annotation.JsonProperty;
21+
import com.google.adk.JsonBaseModel;
22+
import java.util.Objects;
23+
24+
/** Key for a session, composed of appName, userId and session id. */
25+
public final class SessionKey extends JsonBaseModel {
26+
private final String appName;
27+
private final String userId;
28+
private final String id;
29+
30+
@JsonCreator
31+
public SessionKey(
32+
@JsonProperty("appName") String appName,
33+
@JsonProperty("userId") String userId,
34+
@JsonProperty("id") String id) {
35+
this.appName = appName;
36+
this.userId = userId;
37+
this.id = id;
38+
}
39+
40+
@JsonProperty("appName")
41+
public String appName() {
42+
return appName;
43+
}
44+
45+
@JsonProperty("userId")
46+
public String userId() {
47+
return userId;
48+
}
49+
50+
@JsonProperty("id")
51+
public String id() {
52+
return id;
53+
}
54+
55+
@Override
56+
public boolean equals(Object o) {
57+
if (this == o) {
58+
return true;
59+
}
60+
if (o == null || getClass() != o.getClass()) {
61+
return false;
62+
}
63+
SessionKey that = (SessionKey) o;
64+
return Objects.equals(appName, that.appName)
65+
&& Objects.equals(userId, that.userId)
66+
&& Objects.equals(id, that.id);
67+
}
68+
69+
@Override
70+
public int hashCode() {
71+
return Objects.hash(appName, userId, id);
72+
}
73+
74+
@Override
75+
public String toString() {
76+
return toJson();
77+
}
78+
79+
public static SessionKey fromJson(String json) {
80+
return fromJsonString(json, SessionKey.class);
81+
}
82+
}

0 commit comments

Comments
 (0)