Skip to content

Commit 37a5830

Browse files
committed
feat: add verbosity parameter
Signed-off-by: Alexandros Pappas <[email protected]>
1 parent cdf763e commit 37a5830

File tree

3 files changed

+55
-9
lines changed

3 files changed

+55
-9
lines changed

models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ public class OpenAiChatOptions implements ToolCallingChatOptions {
196196
*/
197197
private @JsonProperty("reasoning_effort") String reasoningEffort;
198198

199+
/**
200+
* verbosity: string or null
201+
* Optional - Defaults to medium
202+
* 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.
203+
* Currently supported values are low, medium, and high.
204+
* If specified, the model will use web search to find relevant information to answer the user's question.
205+
*/
206+
private @JsonProperty("verbosity") String verbosity;
207+
199208
/**
200209
* This tool searches the web for relevant results to use in a response.
201210
*/
@@ -268,6 +277,7 @@ public static OpenAiChatOptions fromOptions(OpenAiChatOptions fromOptions) {
268277
.metadata(fromOptions.getMetadata())
269278
.reasoningEffort(fromOptions.getReasoningEffort())
270279
.webSearchOptions(fromOptions.getWebSearchOptions())
280+
.verbosity(fromOptions.getVerbosity())
271281
.build();
272282
}
273283

@@ -564,6 +574,14 @@ public void setWebSearchOptions(WebSearchOptions webSearchOptions) {
564574
this.webSearchOptions = webSearchOptions;
565575
}
566576

577+
public String getVerbosity() {
578+
return this.verbosity;
579+
}
580+
581+
public void setVerbosity(String verbosity) {
582+
this.verbosity = verbosity;
583+
}
584+
567585
@Override
568586
public OpenAiChatOptions copy() {
569587
return OpenAiChatOptions.fromOptions(this);
@@ -609,7 +627,8 @@ public boolean equals(Object o) {
609627
&& Objects.equals(this.outputAudio, other.outputAudio) && Objects.equals(this.store, other.store)
610628
&& Objects.equals(this.metadata, other.metadata)
611629
&& Objects.equals(this.reasoningEffort, other.reasoningEffort)
612-
&& Objects.equals(this.webSearchOptions, other.webSearchOptions);
630+
&& Objects.equals(this.webSearchOptions, other.webSearchOptions)
631+
&& Objects.equals(this.verbosity, other.verbosity);
613632
}
614633

615634
@Override
@@ -802,6 +821,11 @@ public Builder webSearchOptions(WebSearchOptions webSearchOptions) {
802821
return this;
803822
}
804823

824+
public Builder verbosity(String verbosity) {
825+
this.options.verbosity = verbosity;
826+
return this;
827+
}
828+
805829
public OpenAiChatOptions build() {
806830
return this.options;
807831
}

models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ public enum OutputModality {
10851085
* Currently supported values are low, medium, and high. Reducing reasoning effort can
10861086
* result in faster responses and fewer tokens used on reasoning in a response.
10871087
* @param webSearchOptions Options for web search.
1088+
* @param verbosity Controls the verbosity of the model's response.
10881089
*/
10891090
@JsonInclude(Include.NON_NULL)
10901091
public record ChatCompletionRequest(// @formatter:off
@@ -1115,7 +1116,8 @@ public record ChatCompletionRequest(// @formatter:off
11151116
@JsonProperty("parallel_tool_calls") Boolean parallelToolCalls,
11161117
@JsonProperty("user") String user,
11171118
@JsonProperty("reasoning_effort") String reasoningEffort,
1118-
@JsonProperty("web_search_options") WebSearchOptions webSearchOptions) {
1119+
@JsonProperty("web_search_options") WebSearchOptions webSearchOptions,
1120+
@JsonProperty("verbosity") String verbosity) {
11191121

11201122
/**
11211123
* Shortcut constructor for a chat completion request with the given messages, model and temperature.
@@ -1127,7 +1129,7 @@ public record ChatCompletionRequest(// @formatter:off
11271129
public ChatCompletionRequest(List<ChatCompletionMessage> messages, String model, Double temperature) {
11281130
this(messages, model, null, null, null, null, null, null, null, null, null, null, null, null, null,
11291131
null, null, null, false, null, temperature, null,
1130-
null, null, null, null, null, null);
1132+
null, null, null, null, null, null, null);
11311133
}
11321134

11331135
/**
@@ -1141,7 +1143,7 @@ public ChatCompletionRequest(List<ChatCompletionMessage> messages, String model,
11411143
this(messages, model, null, null, null, null, null, null,
11421144
null, null, null, List.of(OutputModality.AUDIO, OutputModality.TEXT), audio, null, null,
11431145
null, null, null, stream, null, null, null,
1144-
null, null, null, null, null, null);
1146+
null, null, null, null, null, null, null);
11451147
}
11461148

11471149
/**
@@ -1156,7 +1158,7 @@ public ChatCompletionRequest(List<ChatCompletionMessage> messages, String model,
11561158
public ChatCompletionRequest(List<ChatCompletionMessage> messages, String model, Double temperature, boolean stream) {
11571159
this(messages, model, null, null, null, null, null, null, null, null, null,
11581160
null, null, null, null, null, null, null, stream, null, temperature, null,
1159-
null, null, null, null, null, null);
1161+
null, null, null, null, null, null, null);
11601162
}
11611163

11621164
/**
@@ -1172,7 +1174,7 @@ public ChatCompletionRequest(List<ChatCompletionMessage> messages, String model,
11721174
List<FunctionTool> tools, Object toolChoice) {
11731175
this(messages, model, null, null, null, null, null, null, null, null, null,
11741176
null, null, null, null, null, null, null, false, null, 0.8, null,
1175-
tools, toolChoice, null, null, null, null);
1177+
tools, toolChoice, null, null, null, null, null);
11761178
}
11771179

11781180
/**
@@ -1185,7 +1187,7 @@ public ChatCompletionRequest(List<ChatCompletionMessage> messages, String model,
11851187
public ChatCompletionRequest(List<ChatCompletionMessage> messages, Boolean stream) {
11861188
this(messages, null, null, null, null, null, null, null, null, null, null,
11871189
null, null, null, null, null, null, null, stream, null, null, null,
1188-
null, null, null, null, null, null);
1190+
null, null, null, null, null, null, null);
11891191
}
11901192

11911193
/**
@@ -1198,7 +1200,7 @@ public ChatCompletionRequest streamOptions(StreamOptions streamOptions) {
11981200
return new ChatCompletionRequest(this.messages, this.model, this.store, this.metadata, this.frequencyPenalty, this.logitBias, this.logprobs,
11991201
this.topLogprobs, this.maxTokens, this.maxCompletionTokens, this.n, this.outputModalities, this.audioParameters, this.presencePenalty,
12001202
this.responseFormat, this.seed, this.serviceTier, this.stop, this.stream, streamOptions, this.temperature, this.topP,
1201-
this.tools, this.toolChoice, this.parallelToolCalls, this.user, this.reasoningEffort, this.webSearchOptions);
1203+
this.tools, this.toolChoice, this.parallelToolCalls, this.user, this.reasoningEffort, this.webSearchOptions, this.verbosity);
12021204
}
12031205

12041206
/**

models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void validateReasoningTokens() {
7777
"If a train travels 100 miles in 2 hours, what is its average speed?", ChatCompletionMessage.Role.USER);
7878
ChatCompletionRequest request = new ChatCompletionRequest(List.of(userMessage), "o1", null, null, null, null,
7979
null, null, null, null, null, null, null, null, null, null, null, null, false, null, null, null, null,
80-
null, null, null, "low", null);
80+
null, null, null, "low", null, null);
8181
ResponseEntity<ChatCompletion> response = this.openAiApi.chatCompletionEntity(request);
8282

8383
assertThat(response).isNotNull();
@@ -172,4 +172,24 @@ void chatCompletionEntityWithNewModels(OpenAiApi.ChatModel modelName) {
172172
assertThat(response.getBody().model()).containsIgnoringCase(modelName.getValue());
173173
}
174174

175+
@ParameterizedTest(name = "{0} : {displayName}")
176+
@EnumSource(names = { "GPT_5_NANO" })
177+
void chatCompletionEntityWithNewModelsAndLowVerbosity(OpenAiApi.ChatModel modelName) {
178+
ChatCompletionMessage chatCompletionMessage = new ChatCompletionMessage(
179+
"What is the answer to the ultimate question of life, the universe, and everything?", Role.USER);
180+
181+
ChatCompletionRequest request = new ChatCompletionRequest(List.of(chatCompletionMessage), // messages
182+
modelName.getValue(), null, null, null, null, null, null, null, null, null, null, null, null, null,
183+
null, null, null, false, null, 1.0, null, null, null, null, null, null, null, "low");
184+
185+
ResponseEntity<ChatCompletion> response = this.openAiApi.chatCompletionEntity(request);
186+
187+
assertThat(response).isNotNull();
188+
assertThat(response.getBody()).isNotNull();
189+
assertThat(response.getBody().choices()).isNotEmpty();
190+
assertThat(response.getBody().choices().get(0).message().content()).isNotEmpty();
191+
assertThat(response.getBody().model()).containsIgnoringCase(modelName.getValue());
192+
193+
}
194+
175195
}

0 commit comments

Comments
 (0)