Description
There are two main issues with the current implementation of the createRequest
method (see additional context below) in the OpenAiChatModel
class:
- Inability to Pass
tool_calls
ortool_call_id
: TheChatCompletionMessage
constructor called in thecreateRequest
method does not allow passingtool_calls
ortool_call_id
. These are always initialized asnull
.
java
public ChatCompletionMessage(Object content, Role role) {
this(content, role, null, null, null);
}
- Discrepancy in Message Roles:
There is a discrepancy between the
MessageType
enum inAbstractMessage
and theRole
enum in OpenAI messages.
MessageType
in AbstractMessage
:
java
public enum MessageType {
USER("user"),
ASSISTANT("assistant"),
SYSTEM("system"),
FUNCTION("function");
}
Role
in OpenAI messages:
java
public enum Role {
@JsonProperty("system")
SYSTEM,
@JsonProperty("user")
USER,
@JsonProperty("assistant")
ASSISTANT,
@JsonProperty("tool")
TOOL
}
This results in in an java.lang.IllegalArgumentException
Steps to Reproduce
Passing a FunctionMessage
in a prompt results in an error:
@Test
public void testPromptWithTool() throws JsonProcessingException {
Prompt prompt = new Prompt(new FunctionMessage(""));
openAiChatModel.call(prompt);
}
Error:
java.lang.IllegalArgumentException: No enum constant org.springframework.ai.openai.api.OpenAiApi.ChatCompletionMessage.Role.FUNCTION
at java.base/java.lang.Enum.valueOf(Enum.java:293)
at org.springframework.ai.openai.api.OpenAiApi$ChatCompletionMessage$Role.valueOf(OpenAiApi.java:488)
at org.springframework.ai.openai.OpenAiChatModel.lambda$createRequest$9(OpenAiChatModel.java:267)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.Collections$2.tryAdvance(Collections.java:5073)
at java.base/java.util.Collections$2.forEachRemaining(Collections.java:5081)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
at org.springframework.ai.openai.OpenAiChatModel.createRequest(OpenAiChatModel.java:268)
at org.springframework.ai.openai.OpenAiChatModel.call(OpenAiChatModel.java:140)
at it.ai.foundation.ServiceChatModelTest.testPromptWithTool(ServiceChatModelTest.java:143)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Expected Behavior
The current implementation of the createRequest
method does not cover all possible prompts that a user could define. The expected behavior should be as follows:
- Support for Tool Calls:
-
The
createRequest
method should allow passingtool_calls
andtool_call_id
to theChatCompletionMessage
constructor. This will enable the proper handling of tool calls within the chat prompt. -
Consistent Message Roles:
- The
MessageType
enum inAbstractMessage
should be aligned with theRole
enum in OpenAI messages. Alternatively, a mapping should be provided to ensure that all message types are correctly translated and no errors occur due to missing enum constants.
Additional Context
Here is the current implementation of the createRequest
method:
ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
Set<String> functionsForThisRequest = new HashSet<>();
List<ChatCompletionMessage> chatCompletionMessages = prompt.getInstructions().stream().map(m -> {
// Add text content.
List<MediaContent> contents = new ArrayList<>(List.of(new MediaContent(m.getContent())));
if (!CollectionUtils.isEmpty(m.getMedia())) {
// Add media content.
contents.addAll(m.getMedia()
.stream()
.map(media -> new MediaContent(
new MediaContent.ImageUrl(this.fromMediaData(media.getMimeType(), media.getData()))))
.toList());
}
return new ChatCompletionMessage(contents, ChatCompletionMessage.Role.valueOf(m.getMessageType().name()));
}).toList();
// rest of the code...
}
Comment From: ThomasVitale
Thanks for reporting this issue. The function calling APIs have been recently refactored and improved. The changes should have handled both points you reported.
- The
ChatCompletionMessage
in theOpenAiApi
class accepts bothtoolCallId
andtoolCalls
. See: https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java#L519 - The
MessageType
enum usestool
as the role name instead offunction
. See: https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/MessageType.java#L51
Can you confirm this issue is fixed?
Comment From: csterwa
@RikJux please let us know if this is resolved. Will close in 7 days if no response. Thank you.
Comment From: csterwa
Closing for now.