java.lang.ClassCastException: DefaultChatOptions cannot be cast to OpenAiChatOptions in Spring AI 2.0
search cancel

java.lang.ClassCastException: DefaultChatOptions cannot be cast to OpenAiChatOptions in Spring AI 2.0

book

Article ID: 445694

calendar_today

Updated On:

Products

VMware Tanzu Spring Essentials

Issue/Introduction

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());

Environment

Spring AI 2.0

Cause

In Spring AI 2.0, significant changes were introduced to how options are merged across abstraction layers.

  • ChatClient: This is the high-level, portable abstraction layer. It handles the translation of generic ChatOptions to provider-specific options automatically.
  • ChatModel: This is a lower-level SPI. At this layer, the model implementations (like 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.

Resolution

To resolve this while maintaining code portability or using the lower-level ChatModel, use one of the following approaches:

1. Use the mutate() pattern

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);


2. Use the combineWith() pattern (Recommended for Portability)

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);


3. Transition to ChatClient (Best Practice)

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();