Hello,
Bug description From 1.0.0-M6, compiling a native image, using OpenAI, fails at runtime with
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalArgumentException: No @JsonProperty fields found in the org.springframework.ai.openai.OpenAiChatOptions] with root cause
java.lang.IllegalArgumentException: No @JsonProperty fields found in the org.springframework.ai.openai.OpenAiChatOptions
at org.springframework.ai.model.ModelOptionsUtils.merge(ModelOptionsUtils.java:167)
at org.springframework.ai.model.ModelOptionsUtils.merge(ModelOptionsUtils.java:199)
at org.springframework.ai.openai.OpenAiChatModel.buildRequestPrompt(OpenAiChatModel.java:579)
[...]
Looks like this check introduced make the thing less plug and play https://github.com/spring-projects/spring-ai/blob/bed1db352a44da91d4a3f1bf2383cbaf5e361247/spring-ai-core/src/main/java/org/springframework/ai/model/ModelOptionsUtils.java#L166-L168
Before this commit, the xxxChatOptions
were not caught by reflection too, but it did not seem to cause any harm on a very simple sample.
IMO, XXXXRuntimeHints
should add reflection hints for every xxxChatOptions
. Happy to PR if this is this is acceptable.
By using the tracing agent, here is what works :
[
{
"name":"org.springframework.ai.openai.OpenAiChatOptions",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getFrequencyPenalty","parameterTypes":[] }, {"name":"getLogitBias","parameterTypes":[] }, {"name":"getLogprobs","parameterTypes":[] }, {"name":"getMaxCompletionTokens","parameterTypes":[] }, {"name":"getMaxTokens","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"getModel","parameterTypes":[] }, {"name":"getN","parameterTypes":[] }, {"name":"getOutputAudio","parameterTypes":[] }, {"name":"getOutputModalities","parameterTypes":[] }, {"name":"getParallelToolCalls","parameterTypes":[] }, {"name":"getPresencePenalty","parameterTypes":[] }, {"name":"getReasoningEffort","parameterTypes":[] }, {"name":"getResponseFormat","parameterTypes":[] }, {"name":"getSeed","parameterTypes":[] }, {"name":"getStop","parameterTypes":[] }, {"name":"getStore","parameterTypes":[] }, {"name":"getStreamOptions","parameterTypes":[] }, {"name":"getStreamUsage","parameterTypes":[] }, {"name":"getTemperature","parameterTypes":[] }, {"name":"getToolChoice","parameterTypes":[] }, {"name":"getTools","parameterTypes":[] }, {"name":"getTopLogprobs","parameterTypes":[] }, {"name":"getTopP","parameterTypes":[] }, {"name":"getUser","parameterTypes":[] }, {"name":"setModel","parameterTypes":["java.lang.String"] }, {"name":"setTemperature","parameterTypes":["java.lang.Double"] }]
}
]
Environment Spring Boot 3.4.3, liberika NIK 23.1.6.r21-nik
Steps to reproduce Use OpenAI API and compile with graalVM
Expected behavior should work out-of-the-box, like in M5. Adding reflections hints for chat options should be enough. Because today, only a few packages are scanned. Example for OpenAI :
@Override
public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader classLoader) {
var mcs = MemberCategory.values();
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiApi.class))) {
hints.reflection().registerType(tr, mcs);
}
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiAudioApi.class))) {
hints.reflection().registerType(tr, mcs);
}
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiImageApi.class))) {
hints.reflection().registerType(tr, mcs);
}
}
But OpenAiChatOptions
contains Jackson
annotations too.
Also, nitpick, but OpenAiApi.class
, OpenAiAudioApi.class
, OpenAiImageApi.class
are all in the same package. Having multiple findJsonAnnotatedClassesInPackage
is useless here ;)