feat: Add reasoningEffort parameter to OpenAI API and Chat Options
This commit introduces the `reasoningEffort` parameter to the OpenAI API integration, allowing control over the reasoning effort used by models like `o1-mini`. Changes: - Adds `reasoningEffort` field to `OpenAiApi.ChatCompletionRequest`. - Adds `reasoningEffort` field and builder method to `OpenAiChatOptions`. Signed-off-by: Alexandros Pappas <apappascs@gmail.com> Signed-off-by: Alexandros Pappas <apappascs@gmail.com>
This commit is contained in:
committed by
Ilayaperumal Gopinathan
parent
b525309e8c
commit
35101e75ae
@@ -182,6 +182,15 @@ public class OpenAiChatOptions implements FunctionCallingOptions {
|
||||
* Developer-defined tags and values used for filtering completions in the <a href="https://platform.openai.com/chat-completions">dashboard</a>.
|
||||
*/
|
||||
private @JsonProperty("metadata") Map<String, String> metadata;
|
||||
|
||||
/**
|
||||
* Constrains effort on reasoning for reasoning models. 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.
|
||||
* Optional. Defaults to medium.
|
||||
* Only for 'o1' models.
|
||||
*/
|
||||
private @JsonProperty("reasoning_effort") String reasoningEffort;
|
||||
|
||||
/**
|
||||
* OpenAI Tool Function Callbacks to register with the ChatModel.
|
||||
* For Prompt Options the functionCallbacks are automatically enabled for the duration of the prompt execution.
|
||||
@@ -256,6 +265,7 @@ public class OpenAiChatOptions implements FunctionCallingOptions {
|
||||
.toolContext(fromOptions.getToolContext())
|
||||
.store(fromOptions.getStore())
|
||||
.metadata(fromOptions.getMetadata())
|
||||
.reasoningEffort(fromOptions.getReasoningEffort())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -520,6 +530,14 @@ public class OpenAiChatOptions implements FunctionCallingOptions {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public String getReasoningEffort() {
|
||||
return this.reasoningEffort;
|
||||
}
|
||||
|
||||
public void setReasoningEffort(String reasoningEffort) {
|
||||
this.reasoningEffort = reasoningEffort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenAiChatOptions copy() {
|
||||
return OpenAiChatOptions.fromOptions(this);
|
||||
@@ -532,7 +550,7 @@ public class OpenAiChatOptions implements FunctionCallingOptions {
|
||||
this.streamOptions, this.seed, this.stop, this.temperature, this.topP, this.tools, this.toolChoice,
|
||||
this.user, this.parallelToolCalls, this.functionCallbacks, this.functions, this.httpHeaders,
|
||||
this.proxyToolCalls, this.toolContext, this.outputModalities, this.outputAudio, this.store,
|
||||
this.metadata);
|
||||
this.metadata, this.reasoningEffort);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -563,7 +581,8 @@ public class OpenAiChatOptions implements FunctionCallingOptions {
|
||||
&& Objects.equals(this.proxyToolCalls, other.proxyToolCalls)
|
||||
&& Objects.equals(this.outputModalities, other.outputModalities)
|
||||
&& Objects.equals(this.outputAudio, other.outputAudio) && Objects.equals(this.store, other.store)
|
||||
&& Objects.equals(this.metadata, other.metadata);
|
||||
&& Objects.equals(this.metadata, other.metadata)
|
||||
&& Objects.equals(this.reasoningEffort, other.reasoningEffort);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -740,6 +759,11 @@ public class OpenAiChatOptions implements FunctionCallingOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder reasoningEffort(String reasoningEffort) {
|
||||
this.options.reasoningEffort = reasoningEffort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OpenAiChatOptions build() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
* @author Mariusz Bernacki
|
||||
* @author Thomas Vitale
|
||||
* @author David Frizelle
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
public class OpenAiApi {
|
||||
|
||||
@@ -913,7 +914,8 @@ public class OpenAiApi {
|
||||
@JsonProperty("tools") List<FunctionTool> tools,
|
||||
@JsonProperty("tool_choice") Object toolChoice,
|
||||
@JsonProperty("parallel_tool_calls") Boolean parallelToolCalls,
|
||||
@JsonProperty("user") String user) {
|
||||
@JsonProperty("user") String user,
|
||||
@JsonProperty("reasoning_effort") String reasoningEffort) {
|
||||
|
||||
/**
|
||||
* Shortcut constructor for a chat completion request with the given messages, model and temperature.
|
||||
@@ -925,7 +927,7 @@ public class OpenAiApi {
|
||||
public ChatCompletionRequest(List<ChatCompletionMessage> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -939,7 +941,7 @@ public class OpenAiApi {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -954,7 +956,7 @@ public class OpenAiApi {
|
||||
public ChatCompletionRequest(List<ChatCompletionMessage> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -970,7 +972,7 @@ public class OpenAiApi {
|
||||
List<FunctionTool> 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);
|
||||
tools, toolChoice, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -983,7 +985,7 @@ public class OpenAiApi {
|
||||
public ChatCompletionRequest(List<ChatCompletionMessage> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -996,7 +998,7 @@ public class OpenAiApi {
|
||||
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.tools, this.toolChoice, this.parallelToolCalls, this.user, this.reasoningEffort);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -40,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Thomas Vitale
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
public class OpenAiApiIT {
|
||||
@@ -66,6 +68,26 @@ public class OpenAiApiIT {
|
||||
assertThat(response.collectList().block()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("The reasoning_effort option is only available in o1 models.")
|
||||
void validateReasoningTokens() {
|
||||
ChatCompletionMessage userMessage = new ChatCompletionMessage(
|
||||
"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");
|
||||
ResponseEntity<ChatCompletion> response = this.openAiApi.chatCompletionEntity(request);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
|
||||
OpenAiApi.Usage.CompletionTokenDetails completionTokenDetails = response.getBody()
|
||||
.usage()
|
||||
.completionTokenDetails();
|
||||
assertThat(completionTokenDetails).isNotNull();
|
||||
assertThat(completionTokenDetails.reasoningTokens()).isPositive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void embeddings() {
|
||||
ResponseEntity<EmbeddingList<Embedding>> response = this.openAiApi
|
||||
|
||||
Reference in New Issue
Block a user