Skip to content

Commit 594d45c

Browse files
committed
Add URL context option to Google GenAI chat
Signed-off-by: Jaehun Choi <jaehun341012@gmail.com>
1 parent d4b6cf2 commit 594d45c

File tree

4 files changed

+79
-3
lines changed

4 files changed

+79
-3
lines changed

models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.ai.google.genai;
1818

19+
import com.google.genai.types.UrlContext;
1920
import java.net.URI;
2021
import java.util.ArrayList;
2122
import java.util.Collection;
@@ -489,6 +490,8 @@ Prompt buildRequestPrompt(Prompt prompt) {
489490
this.defaultOptions.getSafetySettings()));
490491
requestOptions
491492
.setLabels(ModelOptionsUtils.mergeOption(runtimeOptions.getLabels(), this.defaultOptions.getLabels()));
493+
requestOptions.setUrlContextEnabled(ModelOptionsUtils.mergeOption(runtimeOptions.getUrlContextEnabled(),
494+
this.defaultOptions.getUrlContextEnabled()));
492495
}
493496
else {
494497
requestOptions.setInternalToolExecutionEnabled(this.defaultOptions.getInternalToolExecutionEnabled());
@@ -499,6 +502,7 @@ Prompt buildRequestPrompt(Prompt prompt) {
499502
requestOptions.setGoogleSearchRetrieval(this.defaultOptions.getGoogleSearchRetrieval());
500503
requestOptions.setSafetySettings(this.defaultOptions.getSafetySettings());
501504
requestOptions.setLabels(this.defaultOptions.getLabels());
505+
requestOptions.setUrlContextEnabled(this.defaultOptions.getUrlContextEnabled());
502506
}
503507

504508
ToolCallingChatOptions.validateToolCallbacks(requestOptions.getToolCallbacks());
@@ -749,6 +753,14 @@ GeminiRequest createGeminiRequest(Prompt prompt) {
749753
tools.add(googleSearchRetrievalTool);
750754
}
751755

756+
if (prompt.getOptions() instanceof GoogleGenAiChatOptions options && Boolean.TRUE.equals(options.getUrlContextEnabled())) {
757+
final var urlContextTool = Tool.builder()
758+
.urlContext(UrlContext.builder().build())
759+
.build();
760+
761+
tools.add(urlContextTool);
762+
}
763+
752764
if (!CollectionUtils.isEmpty(tools)) {
753765
configBuilder.tools(tools);
754766
}

models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ public class GoogleGenAiChatOptions implements ToolCallingChatOptions, Structure
188188
private Map<String, String> labels = new HashMap<>();
189189
// @formatter:on
190190

191+
/**
192+
* Enable Google's UrlContext tool
193+
*/
194+
@JsonIgnore
195+
private Boolean urlContextEnabled;
196+
191197
public static Builder builder() {
192198
return new Builder();
193199
}
@@ -218,6 +224,7 @@ public static GoogleGenAiChatOptions fromOptions(GoogleGenAiChatOptions fromOpti
218224
options.setUseCachedContent(fromOptions.getUseCachedContent());
219225
options.setAutoCacheThreshold(fromOptions.getAutoCacheThreshold());
220226
options.setAutoCacheTtl(fromOptions.getAutoCacheTtl());
227+
options.setUrlContextEnabled(fromOptions.getUrlContextEnabled());
221228
return options;
222229
}
223230

@@ -459,6 +466,14 @@ public void setOutputSchema(String jsonSchemaText) {
459466
this.setResponseMimeType("application/json");
460467
}
461468

469+
public Boolean getUrlContextEnabled() {
470+
return this.urlContextEnabled;
471+
}
472+
473+
public void setUrlContextEnabled(Boolean urlContextEnabled) {
474+
this.urlContextEnabled = urlContextEnabled;
475+
}
476+
462477
@Override
463478
public boolean equals(Object o) {
464479
if (this == o) {
@@ -481,7 +496,8 @@ public boolean equals(Object o) {
481496
&& Objects.equals(this.toolNames, that.toolNames)
482497
&& Objects.equals(this.safetySettings, that.safetySettings)
483498
&& Objects.equals(this.internalToolExecutionEnabled, that.internalToolExecutionEnabled)
484-
&& Objects.equals(this.toolContext, that.toolContext) && Objects.equals(this.labels, that.labels);
499+
&& Objects.equals(this.toolContext, that.toolContext) && Objects.equals(this.labels, that.labels)
500+
&& Objects.equals(this.urlContextEnabled, that.urlContextEnabled);
485501
}
486502

487503
@Override
@@ -490,7 +506,7 @@ public int hashCode() {
490506
this.frequencyPenalty, this.presencePenalty, this.thinkingBudget, this.maxOutputTokens, this.model,
491507
this.responseMimeType, this.responseSchema, this.toolCallbacks, this.toolNames,
492508
this.googleSearchRetrieval, this.safetySettings, this.internalToolExecutionEnabled, this.toolContext,
493-
this.labels);
509+
this.labels, this.urlContextEnabled);
494510
}
495511

496512
@Override
@@ -502,7 +518,7 @@ public String toString() {
502518
+ this.model + '\'' + ", responseMimeType='" + this.responseMimeType + '\'' + ", toolCallbacks="
503519
+ this.toolCallbacks + ", toolNames=" + this.toolNames + ", googleSearchRetrieval="
504520
+ this.googleSearchRetrieval + ", safetySettings=" + this.safetySettings + ", labels=" + this.labels
505-
+ '}';
521+
+ ", urlContextEnabled=" + this.urlContextEnabled + '}';
506522
}
507523

508524
@Override
@@ -671,6 +687,11 @@ public Builder autoCacheTtl(java.time.Duration autoCacheTtl) {
671687
return this;
672688
}
673689

690+
public Builder urlContextEnabled(boolean enabled) {
691+
this.options.setUrlContextEnabled(enabled);
692+
return this;
693+
}
694+
674695
public GoogleGenAiChatOptions build() {
675696
return this.options;
676697
}

models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/CreateGeminiRequestTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,26 @@ public void createRequestWithGenerationConfigOptions() {
299299
assertThat(request.config().responseMimeType().orElse("")).isEqualTo("application/json");
300300
}
301301

302+
@Test
303+
public void createRequestWithUrlContextToolEnabled() {
304+
305+
var client = GoogleGenAiChatModel.builder()
306+
.genAiClient(this.genAiClient)
307+
.defaultOptions(GoogleGenAiChatOptions.builder().model("DEFAULT_MODEL").urlContextEnabled(true).build())
308+
.build();
309+
310+
GeminiRequest request = client
311+
.createGeminiRequest(client.buildRequestPrompt(new Prompt("Test message content")));
312+
313+
assertThat(request.config().tools()).isPresent();
314+
assertThat(request.config().tools().get()).anySatisfy(tool -> assertThat(tool.urlContext()).isPresent());
315+
316+
request = client.createGeminiRequest(client.buildRequestPrompt(
317+
new Prompt("Test message content", GoogleGenAiChatOptions.builder().urlContextEnabled(false).build())));
318+
319+
assertThat(request.config().tools()).isEmpty();
320+
}
321+
302322
@Test
303323
public void createRequestWithThinkingBudget() {
304324

models/spring-ai-google-genai/src/test/java/org/springframework/ai/google/genai/GoogleGenAiChatOptionsTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,27 @@ public void testLabelsWithEmptyMap() {
167167
assertThat(options.getLabels()).isEmpty();
168168
}
169169

170+
@Test
171+
public void testUrlContextEnabledCopyAndEquality() {
172+
GoogleGenAiChatOptions original = GoogleGenAiChatOptions.builder()
173+
.model("test-model")
174+
.urlContextEnabled(true)
175+
.build();
176+
177+
GoogleGenAiChatOptions copy = original.copy();
178+
179+
assertThat(original.getUrlContextEnabled()).isTrue();
180+
assertThat(copy.getUrlContextEnabled()).isTrue();
181+
assertThat(copy).isEqualTo(original);
182+
assertThat(copy).isNotSameAs(original);
183+
assertThat(copy.toString()).contains("urlContextEnabled=true");
184+
185+
GoogleGenAiChatOptions different = GoogleGenAiChatOptions.builder()
186+
.model("test-model")
187+
.urlContextEnabled(false)
188+
.build();
189+
190+
assertThat(original).isNotEqualTo(different);
191+
assertThat(original.hashCode()).isNotEqualTo(different.hashCode());
192+
}
170193
}

0 commit comments

Comments
 (0)