Skip to content

Commit ed06370

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Add VertexAiSearchTool and AgentTools for search
PiperOrigin-RevId: 848220298
1 parent 9434949 commit ed06370

7 files changed

Lines changed: 425 additions & 0 deletions

File tree

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.adk.events.Event;
2929
import com.google.adk.runner.InMemoryRunner;
3030
import com.google.adk.runner.Runner;
31+
import com.google.common.annotations.VisibleForTesting;
3132
import com.google.common.collect.ImmutableList;
3233
import com.google.common.collect.ImmutableMap;
3334
import com.google.genai.types.Content;
@@ -76,6 +77,11 @@ protected AgentTool(BaseAgent agent, boolean skipSummarization) {
7677
this.skipSummarization = skipSummarization;
7778
}
7879

80+
@VisibleForTesting
81+
BaseAgent getAgent() {
82+
return agent;
83+
}
84+
7985
@Override
8086
public Optional<FunctionDeclaration> declaration() {
8187
FunctionDeclaration.Builder builder =
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2025 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.tools;
18+
19+
import com.google.adk.agents.LlmAgent;
20+
import com.google.adk.models.BaseLlm;
21+
import com.google.common.collect.ImmutableList;
22+
23+
/**
24+
* A tool that wraps a sub-agent that only uses google_search tool.
25+
*
26+
* <p>This is a workaround to support using google_search tool with other tools. TODO(b/448114567):
27+
* Remove once the workaround is no longer needed.
28+
*/
29+
public class GoogleSearchAgentTool extends AgentTool {
30+
31+
public static GoogleSearchAgentTool create(BaseLlm model) {
32+
LlmAgent googleSearchAgent =
33+
LlmAgent.builder()
34+
.name("google_search_agent")
35+
.model(model)
36+
.description("An agent for performing Google search using the `google_search` tool")
37+
.instruction(
38+
" You are a specialized Google search agent.\n"
39+
+ "\n"
40+
+ " When given a search query, use the `google_search` tool to find the"
41+
+ " related information.")
42+
.tools(ImmutableList.of(GoogleSearchTool.INSTANCE))
43+
.build();
44+
return new GoogleSearchAgentTool(googleSearchAgent);
45+
}
46+
47+
protected GoogleSearchAgentTool(LlmAgent agent) {
48+
super(agent, false);
49+
}
50+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2025 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.tools;
18+
19+
import com.google.adk.agents.LlmAgent;
20+
import com.google.adk.models.BaseLlm;
21+
import com.google.common.collect.ImmutableList;
22+
23+
/**
24+
* A tool that wraps a sub-agent that only uses vertex_ai_search tool.
25+
*
26+
* <p>This is a workaround to support using {@link VertexAiSearchTool} tool with other tools.
27+
*/
28+
public class VertexAiSearchAgentTool extends AgentTool {
29+
30+
public static VertexAiSearchAgentTool create(
31+
BaseLlm model, VertexAiSearchTool vertexAiSearchTool) {
32+
LlmAgent vertexAiSearchAgent =
33+
LlmAgent.builder()
34+
.name("vertex_ai_search_agent")
35+
.model(model)
36+
.description(
37+
"An agent for performing Vertex AI search using the `vertex_ai_search` tool")
38+
.instruction(
39+
" You are a specialized Vertex AI search agent.\n"
40+
+ "\n"
41+
+ " When given a search query, use the `vertex_ai_search` tool to find"
42+
+ " the related information.")
43+
.tools(ImmutableList.of(vertexAiSearchTool))
44+
.build();
45+
return new VertexAiSearchAgentTool(vertexAiSearchAgent);
46+
}
47+
48+
protected VertexAiSearchAgentTool(LlmAgent agent) {
49+
super(agent, false);
50+
}
51+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2025 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.tools;
18+
19+
import com.google.adk.models.LlmRequest;
20+
import com.google.auto.value.AutoValue;
21+
import com.google.common.collect.ImmutableList;
22+
import com.google.genai.types.GenerateContentConfig;
23+
import com.google.genai.types.Retrieval;
24+
import com.google.genai.types.Tool;
25+
import com.google.genai.types.VertexAISearch;
26+
import com.google.genai.types.VertexAISearchDataStoreSpec;
27+
import io.reactivex.rxjava3.core.Completable;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
import java.util.Optional;
31+
32+
/**
33+
* A built-in tool using Vertex AI Search.
34+
*
35+
* <p>This tool can be configured with either a {@code dataStoreId} (the Vertex AI search data store
36+
* resource ID) or a {@code searchEngineId} (the Vertex AI search engine resource ID).
37+
*/
38+
@AutoValue
39+
public abstract class VertexAiSearchTool extends BaseTool {
40+
public abstract Optional<String> dataStoreId();
41+
42+
public abstract Optional<List<VertexAISearchDataStoreSpec>> dataStoreSpecs();
43+
44+
public abstract Optional<String> searchEngineId();
45+
46+
public abstract Optional<String> filter();
47+
48+
public abstract Optional<Integer> maxResults();
49+
50+
public abstract Optional<String> project();
51+
52+
public abstract Optional<String> location();
53+
54+
public abstract Optional<String> dataStore();
55+
56+
public static Builder builder() {
57+
return new AutoValue_VertexAiSearchTool.Builder();
58+
}
59+
60+
VertexAiSearchTool() {
61+
super("vertex_ai_search", "vertex_ai_search");
62+
}
63+
64+
@Override
65+
public Completable processLlmRequest(
66+
LlmRequest.Builder llmRequestBuilder, ToolContext toolContext) {
67+
LlmRequest llmRequest = llmRequestBuilder.build();
68+
69+
VertexAISearch.Builder vertexAiSearchBuilder = VertexAISearch.builder();
70+
dataStoreId().ifPresent(vertexAiSearchBuilder::datastore);
71+
searchEngineId().ifPresent(vertexAiSearchBuilder::engine);
72+
filter().ifPresent(vertexAiSearchBuilder::filter);
73+
maxResults().ifPresent(vertexAiSearchBuilder::maxResults);
74+
dataStoreSpecs().ifPresent(vertexAiSearchBuilder::dataStoreSpecs);
75+
76+
Tool retrievalTool =
77+
Tool.builder()
78+
.retrieval(Retrieval.builder().vertexAiSearch(vertexAiSearchBuilder.build()).build())
79+
.build();
80+
81+
List<Tool> currentTools = new ArrayList<>();
82+
if (llmRequest.config().isPresent() && llmRequest.config().get().tools().isPresent()) {
83+
currentTools.addAll(llmRequest.config().get().tools().get());
84+
}
85+
currentTools.add(retrievalTool);
86+
87+
GenerateContentConfig newConfig =
88+
llmRequest
89+
.config()
90+
.map(GenerateContentConfig::toBuilder)
91+
.orElse(GenerateContentConfig.builder())
92+
.tools(ImmutableList.copyOf(currentTools))
93+
.build();
94+
llmRequestBuilder.config(newConfig);
95+
return Completable.complete();
96+
}
97+
98+
/** Builder for {@link VertexAiSearchTool}. */
99+
@AutoValue.Builder
100+
public abstract static class Builder {
101+
public abstract Builder dataStoreId(String dataStoreId);
102+
103+
public abstract Builder dataStoreSpecs(List<VertexAISearchDataStoreSpec> dataStoreSpecs);
104+
105+
public abstract Builder searchEngineId(String searchEngineId);
106+
107+
public abstract Builder filter(String filter);
108+
109+
public abstract Builder maxResults(Integer maxResults);
110+
111+
public abstract Builder project(String project);
112+
113+
public abstract Builder location(String location);
114+
115+
public abstract Builder dataStore(String dataStore);
116+
117+
abstract VertexAiSearchTool autoBuild();
118+
119+
public final VertexAiSearchTool build() {
120+
VertexAiSearchTool tool = autoBuild();
121+
if ((tool.dataStoreId().isEmpty() && tool.searchEngineId().isEmpty())
122+
|| (tool.dataStoreId().isPresent() && tool.searchEngineId().isPresent())) {
123+
throw new IllegalArgumentException(
124+
"Either dataStoreId or searchEngineId must be specified.");
125+
}
126+
if (tool.dataStoreSpecs().isPresent() && tool.searchEngineId().isEmpty()) {
127+
throw new IllegalArgumentException(
128+
"searchEngineId must be specified if dataStoreSpecs is specified.");
129+
}
130+
return tool;
131+
}
132+
}
133+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.google.adk.tools;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
5+
import com.google.adk.agents.LlmAgent;
6+
import com.google.adk.testing.TestLlm;
7+
import com.google.common.collect.ImmutableList;
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.junit.runners.JUnit4;
11+
12+
@RunWith(JUnit4.class)
13+
public final class GoogleSearchAgentToolTest {
14+
15+
@Test
16+
public void create_createsAgent() {
17+
GoogleSearchAgentTool tool = GoogleSearchAgentTool.create(new TestLlm(ImmutableList.of()));
18+
assertThat(((LlmAgent) tool.getAgent()).name()).isEqualTo("google_search_agent");
19+
assertThat(((LlmAgent) tool.getAgent()).tools().blockingGet())
20+
.containsExactly(GoogleSearchTool.INSTANCE);
21+
}
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.google.adk.tools;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
5+
import com.google.adk.agents.LlmAgent;
6+
import com.google.adk.testing.TestLlm;
7+
import com.google.common.collect.ImmutableList;
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.junit.runners.JUnit4;
11+
12+
@RunWith(JUnit4.class)
13+
public final class VertexAiSearchAgentToolTest {
14+
15+
@Test
16+
public void create_createsAgent() {
17+
VertexAiSearchTool vertexAiSearchTool =
18+
VertexAiSearchTool.builder().searchEngineId("test-engine").build();
19+
VertexAiSearchAgentTool tool =
20+
VertexAiSearchAgentTool.create(new TestLlm(ImmutableList.of()), vertexAiSearchTool);
21+
assertThat(((LlmAgent) tool.getAgent()).name()).isEqualTo("vertex_ai_search_agent");
22+
assertThat(((LlmAgent) tool.getAgent()).tools().blockingGet())
23+
.containsExactly(vertexAiSearchTool);
24+
}
25+
}

0 commit comments

Comments
 (0)