diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java
index afbbd803ec6..f7032f37ff3 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java
@@ -196,6 +196,15 @@ public class OpenAiChatOptions implements ToolCallingChatOptions {
*/
private @JsonProperty("reasoning_effort") String reasoningEffort;
+ /**
+ * verbosity: string or null
+ * Optional - Defaults to medium
+ * Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose responses.
+ * Currently supported values are low, medium, and high.
+ * If specified, the model will use web search to find relevant information to answer the user's question.
+ */
+ private @JsonProperty("verbosity") String verbosity;
+
/**
* This tool searches the web for relevant results to use in a response.
*/
@@ -268,6 +277,7 @@ public static OpenAiChatOptions fromOptions(OpenAiChatOptions fromOptions) {
.metadata(fromOptions.getMetadata())
.reasoningEffort(fromOptions.getReasoningEffort())
.webSearchOptions(fromOptions.getWebSearchOptions())
+ .verbosity(fromOptions.getVerbosity())
.build();
}
@@ -564,6 +574,14 @@ public void setWebSearchOptions(WebSearchOptions webSearchOptions) {
this.webSearchOptions = webSearchOptions;
}
+ public String getVerbosity() {
+ return this.verbosity;
+ }
+
+ public void setVerbosity(String verbosity) {
+ this.verbosity = verbosity;
+ }
+
@Override
public OpenAiChatOptions copy() {
return OpenAiChatOptions.fromOptions(this);
@@ -609,7 +627,8 @@ public boolean equals(Object o) {
&& Objects.equals(this.outputAudio, other.outputAudio) && Objects.equals(this.store, other.store)
&& Objects.equals(this.metadata, other.metadata)
&& Objects.equals(this.reasoningEffort, other.reasoningEffort)
- && Objects.equals(this.webSearchOptions, other.webSearchOptions);
+ && Objects.equals(this.webSearchOptions, other.webSearchOptions)
+ && Objects.equals(this.verbosity, other.verbosity);
}
@Override
@@ -802,6 +821,11 @@ public Builder webSearchOptions(WebSearchOptions webSearchOptions) {
return this;
}
+ public Builder verbosity(String verbosity) {
+ this.options.verbosity = verbosity;
+ return this;
+ }
+
public OpenAiChatOptions build() {
return this.options;
}
diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java
index 2cc620481d9..1d02b206cc8 100644
--- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java
+++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java
@@ -482,18 +482,37 @@ public enum ChatModel implements ChatModelDescription {
GPT_5("gpt-5"),
/**
- * GPT-5 (2025-08-07) is a specific snapshot of the GPT-5 model from August
- * 7, 2025, providing enhanced capabilities for complex reasoning and
- * problem-solving tasks.
+ * GPT-5 mini is a faster, more cost-efficient version of GPT-5. It's great for
+ * well-defined tasks and precise prompts.
*
- * Note: GPT-5 models require temperature=1.0 (default value). Custom temperature
- * values are not supported and will cause errors.
+ * Model ID: gpt-5-mini
+ *
+ * See:
+ * gpt-5-mini
+ */
+ GPT_5_MINI("gpt-5-mini"),
+
+ /**
+ * GPT-5 Nano is the fastest, cheapest version of GPT-5. It's great for
+ * summarization and classification tasks.
*
- * Model ID: gpt-5-2025-08-07
+ * Model ID: gpt-5-nano
*
- * See: gpt-5
+ * See:
+ * gpt-5-nano
+ */
+ GPT_5_NANO("gpt-5-nano"),
+
+ /**
+ * GPT-5 Chat points to the GPT-5 snapshot currently used in ChatGPT. GPT-5
+ * accepts both text and image inputs, and produces text outputs.
+ *
+ * Model ID: gpt-5-chat-latest
+ *
+ * See: gpt-5-chat-latest
*/
- GPT_5_2025_08_07("gpt-5-2025-08-07"),
+ GPT_5_CHAT_LATEST("gpt-5-chat-latest"),
/**
* GPT-4o (“o” for “omni”) is the versatile, high-intelligence flagship
@@ -1064,6 +1083,7 @@ public enum OutputModality {
* Currently supported values are low, medium, and high. Reducing reasoning effort can
* result in faster responses and fewer tokens used on reasoning in a response.
* @param webSearchOptions Options for web search.
+ * @param verbosity Controls the verbosity of the model's response.
*/
@JsonInclude(Include.NON_NULL)
public record ChatCompletionRequest(// @formatter:off
@@ -1094,7 +1114,8 @@ public record ChatCompletionRequest(// @formatter:off
@JsonProperty("parallel_tool_calls") Boolean parallelToolCalls,
@JsonProperty("user") String user,
@JsonProperty("reasoning_effort") String reasoningEffort,
- @JsonProperty("web_search_options") WebSearchOptions webSearchOptions) {
+ @JsonProperty("web_search_options") WebSearchOptions webSearchOptions,
+ @JsonProperty("verbosity") String verbosity) {
/**
* Shortcut constructor for a chat completion request with the given messages, model and temperature.
@@ -1106,7 +1127,7 @@ public record ChatCompletionRequest(// @formatter:off
public ChatCompletionRequest(List messages, String model, Double temperature) {
this(messages, model, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, false, null, temperature, null,
- null, null, null, null, null, null);
+ null, null, null, null, null, null, null);
}
/**
@@ -1120,7 +1141,7 @@ public ChatCompletionRequest(List messages, String model,
this(messages, model, null, null, null, null, null, null,
null, null, null, List.of(OutputModality.AUDIO, OutputModality.TEXT), audio, null, null,
null, null, null, stream, null, null, null,
- null, null, null, null, null, null);
+ null, null, null, null, null, null, null);
}
/**
@@ -1135,7 +1156,7 @@ public ChatCompletionRequest(List messages, String model,
public ChatCompletionRequest(List messages, String model, Double temperature, boolean stream) {
this(messages, model, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, stream, null, temperature, null,
- null, null, null, null, null, null);
+ null, null, null, null, null, null, null);
}
/**
@@ -1151,7 +1172,7 @@ public ChatCompletionRequest(List messages, String model,
List tools, Object toolChoice) {
this(messages, model, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, false, null, 0.8, null,
- tools, toolChoice, null, null, null, null);
+ tools, toolChoice, null, null, null, null, null);
}
/**
@@ -1164,7 +1185,7 @@ public ChatCompletionRequest(List messages, String model,
public ChatCompletionRequest(List messages, Boolean stream) {
this(messages, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, stream, null, null, null,
- null, null, null, null, null, null);
+ null, null, null, null, null, null, null);
}
/**
@@ -1177,7 +1198,7 @@ public ChatCompletionRequest streamOptions(StreamOptions streamOptions) {
return new ChatCompletionRequest(this.messages, this.model, this.store, this.metadata, this.frequencyPenalty, this.logitBias, this.logprobs,
this.topLogprobs, this.maxTokens, this.maxCompletionTokens, this.n, this.outputModalities, this.audioParameters, this.presencePenalty,
this.responseFormat, this.seed, this.serviceTier, this.stop, this.stream, streamOptions, this.temperature, this.topP,
- this.tools, this.toolChoice, this.parallelToolCalls, this.user, this.reasoningEffort, this.webSearchOptions);
+ this.tools, this.toolChoice, this.parallelToolCalls, this.user, this.reasoningEffort, this.webSearchOptions, this.verbosity);
}
/**
diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java
index 8b410f422e4..e9fda577c8f 100644
--- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java
+++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java
@@ -77,7 +77,7 @@ void validateReasoningTokens() {
"If a train travels 100 miles in 2 hours, what is its average speed?", ChatCompletionMessage.Role.USER);
ChatCompletionRequest request = new ChatCompletionRequest(List.of(userMessage), "o1", null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, false, null, null, null, null,
- null, null, null, "low", null);
+ null, null, null, "low", null, null);
ResponseEntity response = this.openAiApi.chatCompletionEntity(request);
assertThat(response).isNotNull();
@@ -159,7 +159,7 @@ void streamOutputAudio() {
}
@ParameterizedTest(name = "{0} : {displayName}")
- @EnumSource(names = { "GPT_5", "GPT_5_2025_08_07" })
+ @EnumSource(names = { "GPT_5", "GPT_5_CHAT_LATEST", "GPT_5_MINI", "GPT_5_NANO" })
void chatCompletionEntityWithNewModels(OpenAiApi.ChatModel modelName) {
ChatCompletionMessage chatCompletionMessage = new ChatCompletionMessage("Hello world", Role.USER);
ResponseEntity response = this.openAiApi.chatCompletionEntity(
@@ -172,4 +172,50 @@ void chatCompletionEntityWithNewModels(OpenAiApi.ChatModel modelName) {
assertThat(response.getBody().model()).containsIgnoringCase(modelName.getValue());
}
+ @ParameterizedTest(name = "{0} : {displayName}")
+ @EnumSource(names = { "GPT_5_NANO" })
+ void chatCompletionEntityWithNewModelsAndLowVerbosity(OpenAiApi.ChatModel modelName) {
+ ChatCompletionMessage chatCompletionMessage = new ChatCompletionMessage(
+ "What is the answer to the ultimate question of life, the universe, and everything?", Role.USER);
+
+ ChatCompletionRequest request = new ChatCompletionRequest(List.of(chatCompletionMessage), // messages
+ modelName.getValue(), null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, false, null, 1.0, null, null, null, null, null, null, null, "low");
+
+ ResponseEntity response = this.openAiApi.chatCompletionEntity(request);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getBody()).isNotNull();
+ assertThat(response.getBody().choices()).isNotEmpty();
+ assertThat(response.getBody().choices().get(0).message().content()).isNotEmpty();
+ assertThat(response.getBody().model()).containsIgnoringCase(modelName.getValue());
+ }
+
+ @ParameterizedTest(name = "{0} : {displayName}")
+ @EnumSource(names = { "GPT_5", "GPT_5_MINI", "GPT_5_NANO" })
+ void chatCompletionEntityWithGpt5ModelsAndTemperatureShouldFail(OpenAiApi.ChatModel modelName) {
+ ChatCompletionMessage chatCompletionMessage = new ChatCompletionMessage("Hello world", Role.USER);
+ ChatCompletionRequest request = new ChatCompletionRequest(List.of(chatCompletionMessage), modelName.getValue(),
+ 0.8);
+
+ assertThatThrownBy(() -> this.openAiApi.chatCompletionEntity(request)).isInstanceOf(RuntimeException.class)
+ .hasMessageContaining("Unsupported value");
+ }
+
+ @ParameterizedTest(name = "{0} : {displayName}")
+ @EnumSource(names = { "GPT_5_CHAT_LATEST" })
+ void chatCompletionEntityWithGpt5ChatAndTemperatureShouldSucceed(OpenAiApi.ChatModel modelName) {
+ ChatCompletionMessage chatCompletionMessage = new ChatCompletionMessage("Hello world", Role.USER);
+ ChatCompletionRequest request = new ChatCompletionRequest(List.of(chatCompletionMessage), modelName.getValue(),
+ 0.8);
+
+ ResponseEntity response = this.openAiApi.chatCompletionEntity(request);
+
+ assertThat(response).isNotNull();
+ assertThat(response.getBody()).isNotNull();
+ assertThat(response.getBody().choices()).isNotEmpty();
+ assertThat(response.getBody().choices().get(0).message().content()).isNotEmpty();
+ assertThat(response.getBody().model()).containsIgnoringCase(modelName.getValue());
+ }
+
}
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc
index d13e4dff92a..9d6b3b51175 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc
@@ -181,7 +181,10 @@ The `JSON_SCHEMA` type enables link:https://platform.openai.com/docs/guides/stru
[NOTE]
====
-When using GPT-5 models (`gpt-5`, `gpt-5-2025-08-07`), the temperature parameter must be set to `1.0` (the default value). These models do not support custom temperature values and will return an error if any other temperature value is specified.
+When using GPT-5 models such as `gpt-5`, `gpt-5-mini`, and `gpt-5-nano`, the `temperature` parameter is not supported.
+These models are optimized for reasoning and do not use temperature.
+Specifying a temperature value will result in an error.
+In contrast, conversational models like `gpt-5-chat` do support the `temperature` parameter.
====
NOTE: You can override the common `spring.ai.openai.base-url` and `spring.ai.openai.api-key` for the `ChatModel` and `EmbeddingModel` implementations.