From 6c38b6c6bc4664e8d16d850da8ed7974fe1914b3 Mon Sep 17 00:00:00 2001 From: Enrico Molino Date: Tue, 28 Apr 2026 15:13:53 +0200 Subject: [PATCH] serviceTier support for GoogleGenAi Signed-off-by: Enrico Molino --- .../ai/google/genai/GoogleGenAiChatModel.java | 12 +++++ .../google/genai/GoogleGenAiChatOptions.java | 47 +++++++++++++++---- .../google/genai/GoogleGenAiServiceTier.java | 32 +++++++++++++ pom.xml | 2 +- 4 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiServiceTier.java diff --git a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java index 5b11697100..b5c5748acb 100644 --- a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java +++ b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatModel.java @@ -40,6 +40,7 @@ import com.google.genai.types.Part; import com.google.genai.types.SafetySetting; import com.google.genai.types.Schema; +import com.google.genai.types.ServiceTier; import com.google.genai.types.ThinkingConfig; import com.google.genai.types.ThinkingLevel; import com.google.genai.types.Tool; @@ -771,6 +772,9 @@ GeminiRequest createGeminiRequest(Prompt prompt) { if (requestOptions.getPresencePenalty() != null) { configBuilder.presencePenalty(requestOptions.getPresencePenalty().floatValue()); } + if (requestOptions.getServiceTier() != null) { + configBuilder.serviceTier(mapToGenAiServiceTier(requestOptions.getServiceTier())); + } // Build thinking config if any thinking option is set if (requestOptions.getThinkingBudget() != null || requestOptions.getIncludeThoughts() != null @@ -903,6 +907,14 @@ private static ThinkingLevel mapToGenAiThinkingLevel(GoogleGenAiThinkingLevel le }; } + private static ServiceTier mapToGenAiServiceTier(GoogleGenAiServiceTier tier) { + return switch (tier) { + case FLEX -> new ServiceTier(ServiceTier.Known.FLEX); + case STANDARD -> new ServiceTier(ServiceTier.Known.STANDARD); + case PRIORITY -> new ServiceTier(ServiceTier.Known.PRIORITY); + }; + } + /** * Checks if the model name indicates a Gemini 3 Pro model. * @param modelName the model name to check diff --git a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java index deea481048..773efe07cc 100644 --- a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java +++ b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java @@ -138,6 +138,13 @@ public class GoogleGenAiChatOptions implements ToolCallingChatOptions, Structure */ private GoogleGenAiThinkingLevel thinkingLevel; + /** + * Optional. The service tier to use for the request. + * STANDARD = used by default, FLEX = cost-optimized tier (higher latency), PRIORITY = higher reliability + * but premium price. + */ + private GoogleGenAiServiceTier serviceTier; + /** * Optional. Whether to include extended usage metadata in responses. * When true, includes thinking tokens, cached content, tool-use tokens, and modality details. @@ -216,8 +223,9 @@ protected GoogleGenAiChatOptions(String model, Double frequencyPenalty, Integer Boolean internalToolExecutionEnabled, @Nullable List toolCallbacks, @Nullable Set toolNames, @Nullable Map toolContext, Integer candidateCount, String responseMimeType, String responseSchema, Integer thinkingBudget, Boolean includeThoughts, - GoogleGenAiThinkingLevel thinkingLevel, Boolean includeExtendedUsageMetadata, String cachedContentName, - Boolean useCachedContent, Integer autoCacheThreshold, Duration autoCacheTtl, Boolean googleSearchRetrieval, + GoogleGenAiThinkingLevel thinkingLevel, GoogleGenAiServiceTier serviceTier, + Boolean includeExtendedUsageMetadata, String cachedContentName, Boolean useCachedContent, + Integer autoCacheThreshold, Duration autoCacheTtl, Boolean googleSearchRetrieval, Boolean includeServerSideToolInvocations, List safetySettings, Map labels) { this.model = model; @@ -238,6 +246,7 @@ protected GoogleGenAiChatOptions(String model, Double frequencyPenalty, Integer this.thinkingBudget = thinkingBudget; this.includeThoughts = includeThoughts; this.thinkingLevel = thinkingLevel; + this.serviceTier = serviceTier; this.includeExtendedUsageMetadata = includeExtendedUsageMetadata; this.cachedContentName = cachedContentName; this.useCachedContent = useCachedContent; @@ -420,6 +429,14 @@ public void setThinkingLevel(GoogleGenAiThinkingLevel thinkingLevel) { this.thinkingLevel = thinkingLevel; } + public GoogleGenAiServiceTier getServiceTier() { + return this.serviceTier; + } + + public void setServiceTier(GoogleGenAiServiceTier serviceTier) { + this.serviceTier = serviceTier; + } + public Boolean getIncludeExtendedUsageMetadata() { return this.includeExtendedUsageMetadata; } @@ -532,7 +549,7 @@ public boolean equals(Object o) { && Objects.equals(this.presencePenalty, that.presencePenalty) && Objects.equals(this.thinkingBudget, that.thinkingBudget) && Objects.equals(this.includeThoughts, that.includeThoughts) - && this.thinkingLevel == that.thinkingLevel + && this.thinkingLevel == that.thinkingLevel && this.serviceTier == that.serviceTier && Objects.equals(this.maxOutputTokens, that.maxOutputTokens) && Objects.equals(this.model, that.model) && Objects.equals(this.responseMimeType, that.responseMimeType) && Objects.equals(this.responseSchema, that.responseSchema) @@ -547,9 +564,10 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(this.stopSequences, this.temperature, this.topP, this.topK, this.candidateCount, this.frequencyPenalty, this.presencePenalty, this.thinkingBudget, this.includeThoughts, - this.thinkingLevel, this.maxOutputTokens, this.model, this.responseMimeType, this.responseSchema, - this.toolCallbacks, this.toolNames, this.googleSearchRetrieval, this.includeServerSideToolInvocations, - this.safetySettings, this.internalToolExecutionEnabled, this.toolContext, this.labels); + this.thinkingLevel, this.serviceTier, this.maxOutputTokens, this.model, this.responseMimeType, + this.responseSchema, this.toolCallbacks, this.toolNames, this.googleSearchRetrieval, + this.includeServerSideToolInvocations, this.safetySettings, this.internalToolExecutionEnabled, + this.toolContext, this.labels); } @Override @@ -563,7 +581,7 @@ public String toString() { + this.toolCallbacks + ", toolNames=" + this.toolNames + ", googleSearchRetrieval=" + this.googleSearchRetrieval + ", includeServerSideToolInvocations=" + this.includeServerSideToolInvocations + ", safetySettings=" + this.safetySettings + ", labels=" - + this.labels + '}'; + + this.labels + ", serviceTier=" + this.serviceTier + '}'; } @Override @@ -604,7 +622,8 @@ public Builder mutate() { .googleSearchRetrieval(this.googleSearchRetrieval) .includeServerSideToolInvocations(this.includeServerSideToolInvocations) .safetySettings(this.safetySettings) - .labels(this.labels); + .labels(this.labels) + .serviceTier(this.serviceTier); } public enum TransportType { @@ -648,6 +667,8 @@ public B clone() { protected @Nullable GoogleGenAiThinkingLevel thinkingLevel; + protected @Nullable GoogleGenAiServiceTier serviceTier; + protected @Nullable Boolean includeExtendedUsageMetadata; protected @Nullable String cachedContentName; @@ -767,6 +788,11 @@ public B autoCacheTtl(@Nullable Duration autoCacheTtl) { return self(); } + public B serviceTier(@Nullable GoogleGenAiServiceTier serviceTier) { + this.serviceTier = serviceTier; + return self(); + } + public B combineWith(ChatOptions.Builder other) { super.combineWith(other); if (other instanceof AbstractBuilder that) { @@ -788,6 +814,9 @@ public B combineWith(ChatOptions.Builder other) { if (that.thinkingLevel != null) { this.thinkingLevel = that.thinkingLevel; } + if (that.serviceTier != null) { + this.serviceTier = that.serviceTier; + } if (that.includeExtendedUsageMetadata != null) { this.includeExtendedUsageMetadata = that.includeExtendedUsageMetadata; } @@ -825,7 +854,7 @@ public GoogleGenAiChatOptions build() { this.stopSequences, this.temperature, this.topK, this.topP, this.internalToolExecutionEnabled, this.toolCallbacks, this.toolNames, this.toolContext, this.candidateCount, this.responseMimeType, this.responseSchema, this.thinkingBudget, this.includeThoughts, this.thinkingLevel, - this.includeExtendedUsageMetadata, this.cachedContentName, this.useCachedContent, + this.serviceTier, this.includeExtendedUsageMetadata, this.cachedContentName, this.useCachedContent, this.autoCacheThreshold, this.autoCacheTtl, this.googleSearchRetrieval, this.includeServerSideToolInvocations, this.safetySettings, this.labels); } diff --git a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiServiceTier.java b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiServiceTier.java new file mode 100644 index 0000000000..cd9fdf3ccd --- /dev/null +++ b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiServiceTier.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.google.genai; + +import org.jspecify.annotations.NullMarked; + +/** + * Service Tier for the Google GenAI Chat API. + * + * @author Enrico Molino + * @since 2.0.0 + */ +@NullMarked +public enum GoogleGenAiServiceTier { + + FLEX, STANDARD, PRIORITY + +} diff --git a/pom.xml b/pom.xml index 03c316ac76..37303306c7 100644 --- a/pom.xml +++ b/pom.xml @@ -288,7 +288,7 @@ 0.32.0 1.19.2 26.72.0 - 1.44.0 + 1.51.0 9.20.0 5.0.0 2.2.38