Skip to content

Commit 515028f

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 515028f

4 files changed

Lines changed: 67 additions & 16 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: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,22 @@ 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 = toConcurrentMap(delta);
51+
}
52+
53+
private static ConcurrentMap<String, Object> toConcurrentMap(Map<String, Object> map) {
54+
if (map == null) {
55+
return new ConcurrentHashMap<>();
56+
}
57+
if (map instanceof ConcurrentMap) {
58+
return (ConcurrentMap<String, Object>) map;
59+
}
60+
ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>();
61+
for (Map.Entry<String, Object> entry : map.entrySet()) {
62+
concurrentMap.put(entry.getKey(), entry.getValue() == null ? REMOVED : entry.getValue());
63+
}
64+
return concurrentMap;
5965
}
6066

6167
@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: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ public void constructor_nullDelta_createsEmptyConcurrentHashMap() {
2323
}
2424

2525
@Test
26-
public void constructor_nullState_throwsException() {
27-
Assert.assertThrows(NullPointerException.class, () -> new State(null, new HashMap<>()));
26+
public void constructor_nullState_createsEmptyConcurrentHashMap() {
27+
State state = new State(null, new HashMap<>());
28+
assertThat(state.isEmpty()).isTrue();
29+
assertThat(state.hasDelta()).isFalse();
2830
}
2931

3032
@Test
@@ -47,4 +49,14 @@ public void constructor_singleArgument() {
4749
state.put("key", "value");
4850
assertThat(state.hasDelta()).isTrue();
4951
}
52+
53+
@Test
54+
public void constructor_stateMapWithNullValues_replacesWithRemoved() {
55+
Map<String, Object> stateMap = new HashMap<>();
56+
stateMap.put("key1", "value1");
57+
stateMap.put("key2", null);
58+
State state = new State(stateMap);
59+
assertThat(state).containsEntry("key1", "value1");
60+
assertThat(state).containsEntry("key2", State.REMOVED);
61+
}
5062
}

0 commit comments

Comments
 (0)