Skip to content

Commit e1b7d8b

Browse files
google-genai-botcopybara-github
authored andcommitted
fix: Account for nulls in EventActions and State
This change convert nulls to `State.REMOVED` to be closer to the way other methods in those classes work. PiperOrigin-RevId: 911414129
1 parent 8f20d56 commit e1b7d8b

4 files changed

Lines changed: 60 additions & 20 deletions

File tree

core/src/main/java/com/google/adk/events/EventActions.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,15 @@ public Builder skipSummarization(@Nullable Boolean skipSummarization) {
289289
@CanIgnoreReturnValue
290290
@JsonProperty("stateDelta")
291291
public Builder stateDelta(@Nullable Map<String, Object> value) {
292-
if (value == null) {
293-
this.stateDelta = new ConcurrentHashMap<>();
294-
} else {
295-
this.stateDelta = new ConcurrentHashMap<>(value);
292+
this.stateDelta = new ConcurrentHashMap<>();
293+
if (value != null) {
294+
value
295+
.entrySet()
296+
.forEach(
297+
entry -> {
298+
stateDelta.put(
299+
entry.getKey(), Optional.ofNullable(entry.getValue()).orElse(State.REMOVED));
300+
});
296301
}
297302
return this;
298303
}

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,19 @@ public State(Map<String, Object> state) {
4646

4747
public State(Map<String, Object> state, @Nullable Map<String, Object> delta) {
4848
Objects.requireNonNull(state, "state is null");
49-
this.state =
50-
state instanceof ConcurrentMap
51-
? (ConcurrentMap<String, Object>) state
52-
: new ConcurrentHashMap<>(state);
53-
this.delta =
54-
delta == null
55-
? new ConcurrentHashMap<>()
56-
: delta instanceof ConcurrentMap
57-
? (ConcurrentMap<String, Object>) delta
58-
: new ConcurrentHashMap<>(delta);
49+
this.state = toConcurrentMap(state);
50+
this.delta = delta == null ? new ConcurrentHashMap<>() : toConcurrentMap(delta);
51+
}
52+
53+
private static ConcurrentMap<String, Object> toConcurrentMap(Map<String, Object> map) {
54+
if (map instanceof ConcurrentMap) {
55+
return new ConcurrentHashMap<>((ConcurrentMap<String, Object>) map);
56+
}
57+
ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>();
58+
for (Map.Entry<String, Object> entry : map.entrySet()) {
59+
concurrentMap.put(entry.getKey(), entry.getValue() == null ? REMOVED : entry.getValue());
60+
}
61+
return concurrentMap;
5962
}
6063

6164
@Override

core/src/test/java/com/google/adk/events/EventActionsTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.common.collect.ImmutableSet;
2525
import com.google.genai.types.Content;
2626
import com.google.genai.types.Part;
27+
import java.util.HashMap;
2728
import java.util.Map;
2829
import java.util.concurrent.ConcurrentHashMap;
2930
import java.util.concurrent.ConcurrentMap;
@@ -129,6 +130,33 @@ public void removeStateByKey_marksKeyAsRemoved() {
129130
assertThat(eventActions.stateDelta()).containsExactly("key1", State.REMOVED);
130131
}
131132

133+
@Test
134+
public void builderStateDelta_withNullMap_initializesEmptyMap() {
135+
EventActions eventActions = EventActions.builder().stateDelta(null).build();
136+
137+
assertThat(eventActions.stateDelta()).isEmpty();
138+
}
139+
140+
@Test
141+
public void builderStateDelta_withNullValue_marksKeyAsRemoved() {
142+
Map<String, Object> inputDelta = new HashMap<>();
143+
inputDelta.put("key1", "value1");
144+
inputDelta.put("key2", null);
145+
146+
EventActions eventActions = EventActions.builder().stateDelta(inputDelta).build();
147+
148+
assertThat(eventActions.stateDelta()).containsExactly("key1", "value1", "key2", State.REMOVED);
149+
}
150+
151+
@Test
152+
public void jsonDeserialization_withNullValueInStateDelta_deserializesAsRemoved()
153+
throws Exception {
154+
String json = "{\"stateDelta\":{\"key1\":\"value1\",\"key2\":null}}";
155+
EventActions deserialized = EventActions.fromJsonString(json, EventActions.class);
156+
157+
assertThat(deserialized.stateDelta()).containsExactly("key1", "value1", "key2", State.REMOVED);
158+
}
159+
132160
@Test
133161
public void jsonSerialization_works() throws Exception {
134162
EventActions eventActions =

core/src/test/java/com/google/adk/sessions/StateTest.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.util.Map;
77
import java.util.concurrent.ConcurrentHashMap;
88
import java.util.concurrent.ConcurrentMap;
9-
import org.junit.Assert;
109
import org.junit.Test;
1110
import org.junit.runner.RunWith;
1211
import org.junit.runners.JUnit4;
@@ -22,11 +21,6 @@ public void constructor_nullDelta_createsEmptyConcurrentHashMap() {
2221
assertThat(state.hasDelta()).isTrue();
2322
}
2423

25-
@Test
26-
public void constructor_nullState_throwsException() {
27-
Assert.assertThrows(NullPointerException.class, () -> new State(null, new HashMap<>()));
28-
}
29-
3024
@Test
3125
public void constructor_regularMapState() {
3226
Map<String, Object> stateMap = new HashMap<>();
@@ -47,4 +41,14 @@ public void constructor_singleArgument() {
4741
state.put("key", "value");
4842
assertThat(state.hasDelta()).isTrue();
4943
}
44+
45+
@Test
46+
public void constructor_stateMapWithNullValues_replacesWithRemoved() {
47+
Map<String, Object> stateMap = new HashMap<>();
48+
stateMap.put("key1", "value1");
49+
stateMap.put("key2", null);
50+
State state = new State(stateMap);
51+
assertThat(state).containsEntry("key1", "value1");
52+
assertThat(state).containsEntry("key2", State.REMOVED);
53+
}
5054
}

0 commit comments

Comments
 (0)