Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion core/src/main/java/com/google/adk/tools/AgentTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import com.google.adk.agents.ConfigAgentUtils;
import com.google.adk.agents.ConfigAgentUtils.ConfigurationException;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.RunConfig;
import com.google.adk.agents.RunConfig.StreamingMode;
import com.google.adk.events.Event;
import com.google.adk.plugins.Plugin;
import com.google.adk.runner.InMemoryRunner;
Expand Down Expand Up @@ -186,10 +188,17 @@ public Single<Map<String, Object>> runAsync(Map<String, Object> args, ToolContex
? ImmutableList.of(toolContext.invocationContext().pluginManager())
: ImmutableList.of();
Runner runner = new InMemoryRunner(this.agent, toolContext.agentName(), plugins);
// Follow the caller's RunConfig but run unary: only the last event becomes the result.
RunConfig callerRunConfig = toolContext.invocationContext().runConfig();
RunConfig runConfig =
callerRunConfig.streamingMode() == StreamingMode.NONE
? callerRunConfig
: callerRunConfig.toBuilder().streamingMode(StreamingMode.NONE).build();
return runner
.sessionService()
.createSession(toolContext.agentName(), "tmp-user", toolContext.state(), null)
.flatMapPublisher(session -> runner.runAsync(session.userId(), session.id(), content))
.flatMapPublisher(
session -> runner.runAsync(session.userId(), session.id(), content, runConfig))
.doOnNext(
event -> {
if (event.actions() != null
Expand Down
43 changes: 43 additions & 0 deletions core/src/test/java/com/google/adk/tools/AgentToolTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.google.adk.tools;

import static com.google.adk.testing.TestUtils.createInvocationContext;
import static com.google.adk.testing.TestUtils.createSubAgent;
import static com.google.adk.testing.TestUtils.createTestAgentBuilder;
import static com.google.adk.testing.TestUtils.createTestLlm;
import static com.google.common.truth.Truth.assertThat;
Expand All @@ -26,12 +28,16 @@
import com.google.adk.agents.ConfigAgentUtils.ConfigurationException;
import com.google.adk.agents.InvocationContext;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.RunConfig;
import com.google.adk.agents.RunConfig.StreamingMode;
import com.google.adk.agents.RunConfig.ToolExecutionMode;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.models.LlmResponse;
import com.google.adk.plugins.Plugin;
import com.google.adk.plugins.PluginManager;
import com.google.adk.sessions.InMemorySessionService;
import com.google.adk.sessions.Session;
import com.google.adk.testing.TestBaseAgent;
import com.google.adk.testing.TestLlm;
import com.google.adk.utils.ComponentRegistry;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -871,6 +877,43 @@ public Maybe<Content> beforeRunCallback(InvocationContext invocationContext) {
assertThat(callbackCalled.get()).isFalse();
}

@Test
public void call_propagatesCallerRunConfig() throws Exception {
TestBaseAgent testAgent = createSubAgent("agent_name");
AgentTool agentTool = AgentTool.create(testAgent);
RunConfig runConfig =
RunConfig.builder()
.toolExecutionMode(ToolExecutionMode.SEQUENTIAL)
.maxLlmCalls(7)
.customMetadata(ImmutableMap.of("tier", "x"))
.build();
ToolContext toolContext =
ToolContext.builder(createInvocationContext(testAgent, runConfig)).build();

Map<String, Object> unused =
agentTool.runAsync(ImmutableMap.of("request", "magic"), toolContext).blockingGet();

assertThat(testAgent.getLastInvocationContext().runConfig()).isEqualTo(runConfig);
}

@Test
public void call_withStreamingRunConfig_runsAgentWithoutStreaming() throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind covering BIDI here too, if possible, for example by running this test with both SSE and BIDI? BIDI is the case that would actually break (the nested function calls would go to handleFunctionCallsLive), and right now the test would still pass if the check only handled SSE.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, the existing test now runs with both SSE and BIDI and checks that only streamingMode changes to NONE while maxLlmCalls is preserved. I also temporarily changed the implementation to normalize SSE only: the test failed on BIDI, then passed after restoring the implementation. AgentToolTest passes all 29 tests in both the default-test and basic executions. The PR's testing description now states that both modes are covered.

for (StreamingMode streamingMode : ImmutableList.of(StreamingMode.SSE, StreamingMode.BIDI)) {
TestBaseAgent testAgent = createSubAgent("agent_name");
AgentTool agentTool = AgentTool.create(testAgent);
RunConfig runConfig = RunConfig.builder().streamingMode(streamingMode).maxLlmCalls(7).build();
ToolContext toolContext =
ToolContext.builder(createInvocationContext(testAgent, runConfig)).build();

Map<String, Object> unused =
agentTool.runAsync(ImmutableMap.of("request", "magic"), toolContext).blockingGet();

// Only the streaming mode changes; the other settings still come from the caller.
assertThat(testAgent.getLastInvocationContext().runConfig())
.isEqualTo(runConfig.toBuilder().streamingMode(StreamingMode.NONE).build());
}
}

private ToolContext createToolContext(BaseAgent agent) {
Session session =
sessionService.createSession("test-app", "test-user", null, "test-session").blockingGet();
Expand Down
Loading