When using Spring AI 2.0 with the OpenAI starter, specifically when calling a ChatModel directly, the application throws a java.lang.ClassCastException:
class org.springframework.ai.chat.prompt.DefaultChatOptions cannot be cast to class org.springframework.ai.openai.OpenAiChatOptions
This typically occurs when using a generic builder to create options, such as
var prompt = new Prompt("query", ChatOptions.builder().withModel("####").build());
Spring AI 2.0
In Spring AI 2.0, significant changes were introduced to how options are merged across abstraction layers.
OpenAiChatModel) now expect a concrete instance of their provider-specific options class (e.g., OpenAiChatOptions) to perform internal merging logic.ChatOptions.builder() returns a DefaultChatOptions instance, which the OpenAiChatModel cannot cast to OpenAiChatOptions in version 2.0.
To resolve this while maintaining code portability or using the lower-level ChatModel, use one of the following approaches:
Instead of creating a new generic builder, mutate the default options provided by the model instance. This ensures the correct concrete type is preserved.
var options = chatModel.getDefaultOptions().mutate()
.withTemperature(0.7f)
.build();
var prompt = new Prompt("Your prompt text", options);
If you have a set of generic ChatOptions and want to remain provider-agnostic, use combineWith(). This merges your generic options into the model-specific type.
ChatOptions portableOptions = ChatOptions.builder()
.withModel("####")
.withTemperature(0.7f)
.build();
// Merges generic options into the model-specific OpenAiChatOptions
var mergedOptions = chatModel.getDefaultOptions().combineWith(portableOptions);
var prompt = new Prompt("Your prompt text", mergedOptions);
For most use cases, it is recommended to use the ChatClient API. ChatClient handles the option casting and merging internally, allowing you to use generic ChatOptions without manual conversion.
// ChatClient handles the abstraction for you
chatClient.prompt()
.options(ChatOptions.builder().withTemperature(0.7f).build())
.call()
.content();