spring-projects / spring-projects/spring-ai

Provide a customization hook for RestClient.Builder used by model provider auto-configurations

Open
#5,757 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Hi, thanks for the great project!
I've been using Spring AI and encountered a situation where
a customization hook for the internal RestClient would be helpful.
I'd like to propose the following.

Expected Behavior

Spring AI's model provider auto-configurations should provide a way to customize
the RestClient.Builder before it is used to build the internal RestClient.

For example, users should be able to replace the message converters (and thus
the ObjectMapper) used by the RestClient, configure timeouts, add
interceptors, or set proxy settings — without having to replace the entire
API bean via @ConditionalOnMissingBean.

A common functional interface (e.g., SpringAiRestClientBuilderCustomizer)
accepted by all provider auto-configurations would allow a single bean to
apply customizations across all providers consistently.

Current Behavior

Model provider auto-configurations inject the global RestClient.Builder
and pass it directly to the API builder without any customization hook:

// e.g., OpenAiChatAutoConfiguration.java
restClientBuilderProvider.getIfAvailable(RestClient::builder)

This means the RestClient inherits the application's global ObjectMapper
via Spring Boot's HttpMessageConvertersRestClientCustomizer. While this works
fine for most configurations, certain custom Jackson modules
(e.g., a module that overrides enum deserialization with strict Enum.valueOf())
can break deserialization of model provider DTOs.

Currently, the only workaround is to define a custom API bean (e.g., OpenAiApi)
to replace the auto-configured one entirely, which requires duplicating
all the connection property resolution logic.

Context

In our project, we register a custom Jackson module on the global ObjectMapper
that uses Enum.valueOf() for enum deserialization. This causes Spring AI's
OpenAI API calls to fail because the API response contains lowercase enum values
(e.g., "assistant") while the Java enum constants are uppercase (ASSISTANT):

HttpMessageNotReadableException: JSON parse error:
  No enum constant ...ChatCompletionMessage.Role.assistant
Caused by: java.lang.IllegalArgumentException:
  No enum constant ...Role.assistant
    at java.lang.Enum.valueOf(Enum.java:293)
    at ...AbstractJackson2HttpMessageConverter.readJavaType(...)
    at ...DefaultRestClient.readWithMessageConverters(...)

The same call succeeds when the custom module is not registered.

A customization hook would allow us to isolate the ObjectMapper for
Spring AI's RestClient without replacing the entire API bean:

@Bean
SpringAiRestClientBuilderCustomizer springAiRestClientCustomizer() {
    ObjectMapper isolatedMapper = new ObjectMapper();
    return builder -> builder.messageConverters(converters -> {
        converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter);
        converters.add(new MappingJackson2HttpMessageConverter(isolatedMapper));
    });
}

This pattern is consistent with Spring Boot's existing customizer conventions
(e.g., RestClientCustomizer, WebClientCustomizer).

I'd be happy to submit a PR if this approach aligns with the project's direction.
Thank you!

Related Issues

  • #512 — HTTP Client configuration for models and vector stores

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with OpenAiChatAutoConfiguration and the RestClient::builder usage shown in the issue, then compare the other model-provider auto-configurations and read related issue #512. Trace how RestClient.Builder reaches each API builder. Done means a common customization hook works consistently across providers without replacing their API beans, with relevant tests covering customization.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
api, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.