Skip to content

Commit fee319b

Browse files
google-genai-botcopybara-github
authored andcommitted
refactor: Example tool and utils: replace Optional<List<T>> with List<T>, add recommended test coverage
PiperOrigin-RevId: 859710282
1 parent 55144ac commit fee319b

File tree

3 files changed

+136
-6
lines changed

3 files changed

+136
-6
lines changed

core/src/main/java/com/google/adk/tools/ExampleTool.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
public final class ExampleTool extends BaseTool {
4848

4949
private final Optional<BaseExampleProvider> exampleProvider;
50-
private final Optional<List<Example>> examples;
50+
private final List<Example> examples;
5151

5252
/** Single private constructor; create via builder or fromConfig. */
5353
private ExampleTool(Builder builder) {
@@ -57,7 +57,7 @@ private ExampleTool(Builder builder) {
5757
? "Adds few-shot examples to the request"
5858
: builder.description);
5959
this.exampleProvider = builder.provider;
60-
this.examples = builder.examples.isEmpty() ? Optional.empty() : Optional.of(builder.examples);
60+
this.examples = builder.examples;
6161
}
6262

6363
@Override
@@ -77,9 +77,9 @@ public Completable processLlmRequest(
7777
final String examplesBlock;
7878
if (exampleProvider.isPresent()) {
7979
examplesBlock = ExampleUtils.buildExampleSi(exampleProvider.get(), query);
80-
} else if (examples.isPresent()) {
80+
} else if (!examples.isEmpty()) {
8181
// Adapter provider that returns a fixed list irrespective of query
82-
BaseExampleProvider provider = q -> examples.get();
82+
BaseExampleProvider provider = (unusedQuery) -> examples;
8383
examplesBlock = ExampleUtils.buildExampleSi(provider, query);
8484
} else {
8585
return Completable.complete();
@@ -157,6 +157,7 @@ public static Builder builder() {
157157
return new Builder();
158158
}
159159

160+
/** Builder for {@link ExampleTool}. */
160161
public static final class Builder {
161162
private final List<Example> examples = new ArrayList<>();
162163
private String name = "example_tool";
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.adk.flows.llmflows;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import com.google.adk.agents.InvocationContext;
22+
import com.google.adk.agents.LlmAgent;
23+
import com.google.adk.agents.RunConfig;
24+
import com.google.adk.examples.BaseExampleProvider;
25+
import com.google.adk.examples.Example;
26+
import com.google.adk.models.LlmRequest;
27+
import com.google.common.collect.ImmutableList;
28+
import com.google.genai.types.Content;
29+
import com.google.genai.types.Part;
30+
import java.util.List;
31+
import org.junit.Test;
32+
import org.junit.runner.RunWith;
33+
import org.junit.runners.JUnit4;
34+
35+
@RunWith(JUnit4.class)
36+
public final class ExamplesTest {
37+
38+
private static class TestExampleProvider implements BaseExampleProvider {
39+
@Override
40+
public List<Example> getExamples(String query) {
41+
return ImmutableList.of(
42+
Example.builder()
43+
.input(Content.fromParts(Part.fromText("input1")))
44+
.output(
45+
ImmutableList.of(
46+
Content.builder().parts(Part.fromText("output1")).role("model").build()))
47+
.build());
48+
}
49+
}
50+
51+
@Test
52+
public void processRequest_withExampleProvider_addsExamplesToInstructions() {
53+
LlmAgent agent =
54+
LlmAgent.builder().name("test-agent").exampleProvider(new TestExampleProvider()).build();
55+
InvocationContext context =
56+
InvocationContext.builder()
57+
.invocationId("invocation1")
58+
.agent(agent)
59+
.userContent(Content.fromParts(Part.fromText("what is up?")))
60+
.runConfig(RunConfig.builder().build())
61+
.build();
62+
LlmRequest request = LlmRequest.builder().build();
63+
Examples examplesProcessor = new Examples();
64+
65+
RequestProcessor.RequestProcessingResult result =
66+
examplesProcessor.processRequest(context, request).blockingGet();
67+
68+
assertThat(result.updatedRequest().getSystemInstructions()).isNotEmpty();
69+
assertThat(result.updatedRequest().getSystemInstructions().get(0))
70+
.contains("[user]\ninput1\n\n[model]\noutput1\n");
71+
}
72+
73+
@Test
74+
public void processRequest_withoutExampleProvider_doesNotAddExamplesToInstructions() {
75+
LlmAgent agent = LlmAgent.builder().name("test-agent").build();
76+
InvocationContext context =
77+
InvocationContext.builder()
78+
.invocationId("invocation1")
79+
.agent(agent)
80+
.userContent(Content.fromParts(Part.fromText("what is up?")))
81+
.runConfig(RunConfig.builder().build())
82+
.build();
83+
LlmRequest request = LlmRequest.builder().build();
84+
Examples examplesProcessor = new Examples();
85+
86+
RequestProcessor.RequestProcessingResult result =
87+
examplesProcessor.processRequest(context, request).blockingGet();
88+
89+
assertThat(result.updatedRequest().getSystemInstructions()).isEmpty();
90+
}
91+
}

core/src/test/java/com/google/adk/tools/ExampleToolTest.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,43 @@ public void processLlmRequest_withInlineExamples_appendsFewShot() {
7171
assertThat(si).contains("qout");
7272
}
7373

74+
@Test
75+
public void processLlmRequest_withProvider_appendsFewShot() {
76+
ExampleTool tool = ExampleTool.builder().setExampleProvider(ProviderHolder.EXAMPLES).build();
77+
78+
InvocationContext ctx = newContext();
79+
LlmRequest.Builder builder = LlmRequest.builder().model("gemini-2.0-flash");
80+
81+
tool.processLlmRequest(builder, ToolContext.builder(ctx).build()).blockingAwait();
82+
LlmRequest updated = builder.build();
83+
84+
assertThat(updated.getSystemInstructions()).isNotEmpty();
85+
String si = String.join("\n", updated.getSystemInstructions());
86+
assertThat(si).contains("Begin few-shot");
87+
assertThat(si).contains("qin");
88+
assertThat(si).contains("qout");
89+
}
90+
91+
@Test
92+
public void processLlmRequest_withEmptyUserContent_doesNotAppendFewShot() {
93+
ExampleTool tool = ExampleTool.builder().addExample(makeExample("qin", "qout")).build();
94+
InvocationContext ctxWithContent = newContext();
95+
InvocationContext ctx =
96+
InvocationContext.builder()
97+
.invocationId(ctxWithContent.invocationId())
98+
.agent(ctxWithContent.agent())
99+
.session(ctxWithContent.session())
100+
.userContent(Content.fromParts(Part.fromText("")))
101+
.runConfig(ctxWithContent.runConfig())
102+
.build();
103+
LlmRequest.Builder builder = LlmRequest.builder().model("gemini-2.0-flash");
104+
105+
tool.processLlmRequest(builder, ToolContext.builder(ctx).build()).blockingAwait();
106+
LlmRequest updated = builder.build();
107+
108+
assertThat(updated.getSystemInstructions()).isEmpty();
109+
}
110+
74111
@Test
75112
public void fromConfig_withInlineExamples_buildsTool() throws Exception {
76113
BaseTool.ToolArgsConfig args = new BaseTool.ToolArgsConfig();
@@ -95,7 +132,7 @@ public void fromConfig_withInlineExamples_buildsTool() throws Exception {
95132
/** Holder for a provider referenced via ClassName.FIELD reflection. */
96133
static final class ProviderHolder {
97134
public static final BaseExampleProvider EXAMPLES =
98-
(query) -> ImmutableList.of(makeExample("qin", "qout"));
135+
(unusedQuery) -> ImmutableList.of(makeExample("qin", "qout"));
99136

100137
private ProviderHolder() {}
101138
}
@@ -255,7 +292,8 @@ public void fromConfig_withWrongTypeProviderField_throwsConfigurationException()
255292
/** Holder with non-static field for testing. */
256293
static final class NonStaticProviderHolder {
257294
@SuppressWarnings("ConstantField") // Intentionally non-static for testing
258-
public final BaseExampleProvider INSTANCE = (query) -> ImmutableList.of(makeExample("q", "a"));
295+
public final BaseExampleProvider INSTANCE =
296+
(unusedQuery) -> ImmutableList.of(makeExample("q", "a"));
259297

260298
private NonStaticProviderHolder() {}
261299
}

0 commit comments

Comments
 (0)