Skip to content

Commit d0b65ed

Browse files
jdomingrJuan Dominguez
andauthored
feat(genai): add Vertex AI Search tool sample (#10150)
* feat: add new GenAI Vais tool sample * refactor: change the way the datastore string was consctructed for better readibility * include mockito in pom and change tool vais test to mock it instead of using an actual datastore --------- Co-authored-by: Juan Dominguez <[email protected]>
1 parent 22680c4 commit d0b65ed

File tree

3 files changed

+137
-1
lines changed

3 files changed

+137
-1
lines changed

genai/snippets/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
<scope>test</scope>
6060
<version>4.13.2</version>
6161
</dependency>
62+
<dependency>
63+
<groupId>org.mockito</groupId>
64+
<artifactId>mockito-core</artifactId>
65+
<version>5.19.0</version>
66+
<scope>test</scope>
67+
</dependency>
6268
<dependency>
6369
<groupId>com.google.truth</groupId>
6470
<artifactId>truth</artifactId>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 genai.tools;
18+
19+
// [START googlegenaisdk_tools_vais_with_txt]
20+
21+
import com.google.genai.Client;
22+
import com.google.genai.types.GenerateContentConfig;
23+
import com.google.genai.types.GenerateContentResponse;
24+
import com.google.genai.types.HttpOptions;
25+
import com.google.genai.types.Retrieval;
26+
import com.google.genai.types.Tool;
27+
import com.google.genai.types.VertexAISearch;
28+
29+
public class ToolsVaisWithText {
30+
31+
public static void main(String[] args) {
32+
// TODO(developer): Replace these variables before running the sample.
33+
String modelId = "gemini-2.5-flash";
34+
// Load Data Store ID from Vertex AI Search
35+
// E.g datastoreId =
36+
// "projects/project-id/locations/global/collections/default_collection/dataStores/datastore-id"
37+
String datastoreId = "your-datastore";
38+
generateContent(modelId, datastoreId);
39+
}
40+
41+
// Generates text with Vertex AI Search tool
42+
public static String generateContent(String modelId, String datastoreId) {
43+
// Initialize client that will be used to send requests. This client only needs to be created
44+
// once, and can be reused for multiple requests.
45+
try (Client client =
46+
Client.builder()
47+
.location("global")
48+
.vertexAI(true)
49+
.httpOptions(HttpOptions.builder().apiVersion("v1").build())
50+
.build()) {
51+
52+
// Set the VertexAI Search tool and the datastore that the model can use to retrieve data from
53+
Tool vaisSearchTool =
54+
Tool.builder()
55+
.retrieval(
56+
Retrieval.builder()
57+
.vertexAiSearch(VertexAISearch.builder().datastore(datastoreId).build())
58+
.build())
59+
.build();
60+
61+
// Create a GenerateContentConfig and set the Vertex AI Search tool
62+
GenerateContentConfig contentConfig =
63+
GenerateContentConfig.builder().tools(vaisSearchTool).build();
64+
65+
GenerateContentResponse response =
66+
client.models.generateContent(
67+
modelId, "How do I make an appointment to renew my driver's license?", contentConfig);
68+
69+
System.out.print(response.text());
70+
// Example response:
71+
// The process for making an appointment to renew your driver's license varies depending
72+
// on your location. To provide you with the most accurate instructions...
73+
return response.text();
74+
}
75+
}
76+
}
77+
// [END googlegenaisdk_tools_vais_with_txt]

genai/snippets/src/test/java/genai/tools/ToolsIT.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,31 @@
1818

1919
import static com.google.common.truth.Truth.assertThat;
2020
import static com.google.common.truth.Truth.assertWithMessage;
21-
21+
import static org.mockito.ArgumentMatchers.any;
22+
import static org.mockito.ArgumentMatchers.anyString;
23+
import static org.mockito.Mockito.RETURNS_SELF;
24+
import static org.mockito.Mockito.mock;
25+
import static org.mockito.Mockito.mockStatic;
26+
import static org.mockito.Mockito.times;
27+
import static org.mockito.Mockito.verify;
28+
import static org.mockito.Mockito.when;
29+
30+
import com.google.genai.Client;
31+
import com.google.genai.Models;
32+
import com.google.genai.types.GenerateContentConfig;
33+
import com.google.genai.types.GenerateContentResponse;
2234
import java.io.ByteArrayOutputStream;
2335
import java.io.IOException;
2436
import java.io.PrintStream;
37+
import java.lang.reflect.Field;
2538
import org.junit.After;
2639
import org.junit.Before;
2740
import org.junit.BeforeClass;
2841
import org.junit.Test;
2942
import org.junit.runner.RunWith;
3043
import org.junit.runners.JUnit4;
44+
import org.mockito.MockedStatic;
45+
3146

3247
@RunWith(JUnit4.class)
3348
public class ToolsIT {
@@ -105,4 +120,42 @@ public void testToolsGoogleSearchWithText() {
105120
assertThat(response).isNotEmpty();
106121
}
107122

123+
@Test
124+
public void testToolsVaisWithText() throws NoSuchFieldException, IllegalAccessException {
125+
String response = "The process for making an appointment to renew your driver's license"
126+
+ " varies depending on your location.";
127+
128+
String datastore =
129+
String.format(
130+
"projects/%s/locations/global/collections/default_collection/"
131+
+ "dataStores/grounding-test-datastore",
132+
PROJECT_ID);
133+
134+
Client.Builder mockedBuilder = mock(Client.Builder.class, RETURNS_SELF);
135+
Client mockedClient = mock(Client.class);
136+
Models mockedModels = mock(Models.class);
137+
GenerateContentResponse mockedResponse = mock(GenerateContentResponse.class);
138+
139+
try (MockedStatic<Client> mockedStatic = mockStatic(Client.class)) {
140+
mockedStatic.when(Client::builder).thenReturn(mockedBuilder);
141+
when(mockedBuilder.build()).thenReturn(mockedClient);
142+
143+
// Using reflection because 'models' is a final field and cannot be mockable directly
144+
Field field = Client.class.getDeclaredField("models");
145+
field.setAccessible(true);
146+
field.set(mockedClient, mockedModels);
147+
148+
when(mockedClient.models.generateContent(
149+
anyString(), anyString(), any(GenerateContentConfig.class)))
150+
.thenReturn(mockedResponse);
151+
when(mockedResponse.text()).thenReturn(response);
152+
153+
String generatedResponse = ToolsVaisWithText.generateContent(GEMINI_FLASH, datastore);
154+
155+
verify(mockedClient.models, times(1))
156+
.generateContent(anyString(), anyString(), any(GenerateContentConfig.class));
157+
assertThat(generatedResponse).isNotEmpty();
158+
assertThat(response).isEqualTo(generatedResponse);
159+
}
160+
}
108161
}

0 commit comments

Comments
 (0)