Skip to content

Commit 3e6b915

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Add onRunErrorCallback to ADK Plugin and Runner
PiperOrigin-RevId: 963992173
1 parent d7355a7 commit 3e6b915

4 files changed

Lines changed: 70 additions & 1 deletion

File tree

‎core/src/main/java/com/google/adk/plugins/Plugin.java‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ default Completable afterRunCallback(InvocationContext invocationContext) {
8787
return Completable.complete();
8888
}
8989

90+
/**
91+
* Callback executed when a run encounters an error.
92+
*
93+
* @param invocationContext The context for the entire invocation.
94+
* @param error The exception that was raised.
95+
*/
96+
default Completable onRunErrorCallback(InvocationContext invocationContext, Throwable error) {
97+
return Completable.complete();
98+
}
99+
90100
/**
91101
* Method executed when the runner is closed.
92102
*

‎core/src/main/java/com/google/adk/plugins/PluginManager.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,27 @@ public Completable afterRunCallback(InvocationContext invocationContext) {
143143
.compose(Tracing.withContext(capturedContext));
144144
}
145145

146+
public Completable runOnRunErrorCallback(InvocationContext invocationContext, Throwable error) {
147+
return onRunErrorCallback(invocationContext, error);
148+
}
149+
150+
@Override
151+
public Completable onRunErrorCallback(InvocationContext invocationContext, Throwable error) {
152+
Context capturedContext = Context.current();
153+
return Flowable.fromIterable(plugins)
154+
.concatMapCompletable(
155+
plugin ->
156+
plugin
157+
.onRunErrorCallback(invocationContext, error)
158+
.doOnError(
159+
e ->
160+
logger.error(
161+
"[{}] Error during callback 'onRunErrorCallback'",
162+
plugin.getName(),
163+
e)))
164+
.compose(Tracing.withContext(capturedContext));
165+
}
166+
146167
@Override
147168
public Completable close() {
148169
Context capturedContext = Context.current();

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,13 @@ protected Flowable<Event> runAsyncImpl(
557557
.flatMapPublisher(
558558
event ->
559559
runAgentWithUpdatedSession(initialContext, session, event, rootAgent)
560-
.compose(Tracing.<Event>withContext(capturedContext)));
560+
.compose(Tracing.<Event>withContext(capturedContext)))
561+
.doOnError(
562+
throwable ->
563+
this.pluginManager
564+
.runOnRunErrorCallback(initialContext, throwable)
565+
.onErrorComplete()
566+
.subscribe());
561567
})
562568
.doOnError(
563569
throwable -> {
@@ -802,6 +808,10 @@ protected Flowable<Event> runLiveImpl(
802808
Span span = Span.current();
803809
span.setStatus(StatusCode.ERROR, "Error in runLive Flowable execution");
804810
span.recordException(throwable);
811+
this.pluginManager
812+
.runOnRunErrorCallback(invocationContext, throwable)
813+
.onErrorComplete()
814+
.subscribe();
805815
})
806816
.compose(Tracing.<Event>withContext(capturedContext));
807817
});

‎core/src/test/java/com/google/adk/runner/RunnerTest.java‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import static java.util.concurrent.TimeUnit.SECONDS;
3232
import static org.mockito.ArgumentMatchers.any;
3333
import static org.mockito.ArgumentMatchers.anyString;
34+
import static org.mockito.ArgumentMatchers.eq;
3435
import static org.mockito.Mockito.CALLS_REAL_METHODS;
3536
import static org.mockito.Mockito.mock;
3637
import static org.mockito.Mockito.never;
@@ -370,6 +371,33 @@ public void afterRunCallback_error() {
370371
verify(plugin).afterRunCallback(any());
371372
}
372373

374+
@Test
375+
public void onRunErrorCallback_isCalled() {
376+
Exception exception = new Exception("test run error");
377+
TestLlm failingTestLlm = createTestLlm(Flowable.error(exception));
378+
LlmAgent failingAgent = createTestAgentBuilder(failingTestLlm).build();
379+
380+
Runner failingRunner =
381+
Runner.builder()
382+
.app(
383+
App.builder()
384+
.name("test")
385+
.rootAgent(failingAgent)
386+
.plugins(ImmutableList.of(plugin))
387+
.build())
388+
.sessionService(this.runner.sessionService())
389+
.build();
390+
391+
when(plugin.onRunErrorCallback(any(), any())).thenReturn(Completable.complete());
392+
393+
failingRunner
394+
.runAsync("user", session.id(), createContent("from user"))
395+
.test()
396+
.assertError(exception);
397+
398+
verify(plugin).onRunErrorCallback(any(), eq(exception));
399+
}
400+
373401
@Test
374402
public void onUserMessageCallback_success() {
375403
when(plugin.onUserMessageCallback(any(), any())).thenReturn(Maybe.just(pluginContent));

0 commit comments

Comments
 (0)