Skip to content

Commit 617dff4

Browse files
committed
fix(events): treat an error event without function calls as a final response
Matches adk-python: a complete event carrying an error code ends the turn unless the model still asked for tools, even when it holds a function response.
1 parent 4092a1f commit 617dff4

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

‎core/src/main/java/com/google/adk/events/Event.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ public final boolean finalResponse() {
353353
if (actions().skipSummarization().orElse(false) || hasPendingLongRunningToolCall()) {
354354
return true;
355355
}
356+
// An error ends the turn unless the model still asked for tools; mirrors Python ADK.
357+
if (errorCode().isPresent() && !partial().orElse(false) && functionCalls().isEmpty()) {
358+
return true;
359+
}
356360
return functionCalls().isEmpty()
357361
&& functionResponses().isEmpty()
358362
&& !partial().orElse(false)

‎core/src/test/java/com/google/adk/events/EventTest.java‎

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.common.collect.ImmutableList;
2222
import com.google.common.collect.ImmutableMap;
2323
import com.google.common.collect.ImmutableSet;
24+
import com.google.genai.types.CodeExecutionResult;
2425
import com.google.genai.types.Content;
2526
import com.google.genai.types.FinishReason;
2627
import com.google.genai.types.FunctionCall;
@@ -330,4 +331,113 @@ public void finalResponse_returnsTrueIfSkipSummarization() {
330331
.build();
331332
assertThat(event.finalResponse()).isTrue();
332333
}
334+
335+
@Test
336+
public void finalResponse_isTrueForErrorEvent() {
337+
Event event =
338+
Event.builder()
339+
.id("e1")
340+
.invocationId("i1")
341+
.author("agent")
342+
.content(Content.fromParts(Part.fromText("hello")))
343+
.errorCode(new FinishReason("SOME_ERROR"))
344+
.errorMessage("Something went wrong")
345+
.build();
346+
assertThat(event.finalResponse()).isTrue();
347+
}
348+
349+
@Test
350+
public void finalResponse_isTrueForErrorEventWithFunctionResponse() {
351+
Event event =
352+
Event.builder()
353+
.id("e1")
354+
.invocationId("i1")
355+
.author("agent")
356+
.content(
357+
Content.fromParts(Part.fromFunctionResponse("tool", ImmutableMap.of("k", "v"))))
358+
.errorCode(new FinishReason("SOME_ERROR"))
359+
.errorMessage("Something went wrong")
360+
.build();
361+
assertThat(event.finalResponse()).isTrue();
362+
}
363+
364+
@Test
365+
public void finalResponse_isFalseForErrorEventWithFunctionCall() {
366+
Event event =
367+
Event.builder()
368+
.id("e1")
369+
.invocationId("i1")
370+
.author("agent")
371+
.content(Content.fromParts(Part.fromFunctionCall("tool", ImmutableMap.of("k", "v"))))
372+
.errorCode(new FinishReason("SOME_ERROR"))
373+
.errorMessage("Something went wrong")
374+
.build();
375+
assertThat(event.finalResponse()).isFalse();
376+
}
377+
378+
@Test
379+
public void finalResponse_isFalseForPartialErrorEvent() {
380+
Event event =
381+
Event.builder()
382+
.id("e1")
383+
.invocationId("i1")
384+
.author("agent")
385+
.content(Content.fromParts(Part.fromText("hello")))
386+
.errorCode(new FinishReason("SOME_ERROR"))
387+
.errorMessage("Something went wrong")
388+
.partial(true)
389+
.build();
390+
assertThat(event.finalResponse()).isFalse();
391+
}
392+
393+
@Test
394+
public void finalResponse_isFalseForFunctionResponse() {
395+
Event event =
396+
Event.builder()
397+
.id("e1")
398+
.invocationId("i1")
399+
.author("agent")
400+
.content(
401+
Content.fromParts(Part.fromFunctionResponse("tool", ImmutableMap.of("k", "v"))))
402+
.build();
403+
assertThat(event.finalResponse()).isFalse();
404+
}
405+
406+
@Test
407+
public void finalResponse_isFalseForTrailingCodeExecutionResult() {
408+
Event event =
409+
Event.builder()
410+
.id("e1")
411+
.invocationId("i1")
412+
.author("agent")
413+
.content(
414+
Content.fromParts(
415+
Part.fromText("hello"),
416+
Part.builder()
417+
.codeExecutionResult(
418+
CodeExecutionResult.builder().outcome("OK").output("42").build())
419+
.build()))
420+
.build();
421+
assertThat(event.finalResponse()).isFalse();
422+
}
423+
424+
@Test
425+
public void finalResponse_isTrueForErrorEventWithTrailingCodeExecutionResult() {
426+
Event event =
427+
Event.builder()
428+
.id("e1")
429+
.invocationId("i1")
430+
.author("agent")
431+
.content(
432+
Content.fromParts(
433+
Part.fromText("hello"),
434+
Part.builder()
435+
.codeExecutionResult(
436+
CodeExecutionResult.builder().outcome("OK").output("42").build())
437+
.build()))
438+
.errorCode(new FinishReason("SOME_ERROR"))
439+
.errorMessage("Something went wrong")
440+
.build();
441+
assertThat(event.finalResponse()).isTrue();
442+
}
333443
}

0 commit comments

Comments
 (0)