spring-projects / spring-ai

An Application Framework for AI Engineering
https://docs.spring.io/spring-ai/reference/index.html
Apache License 2.0
3.13k stars 779 forks source link

Error When Registering Plain ObjectMapper as Bean in Spring AI #1547

Open yeseong0412 opened 5 days ago

yeseong0412 commented 5 days ago

Bug Description

When using the Spring AI library and directly registering an ObjectMapper as bean, RestClientException and UnrecognizedPropertyException occur. This error does not happen when using the ObjectMapper bean provided by Spring internally.

The error message is as follows:

org.springframework.web.client.RestClientException: Error while extracting response for type [org.springframework.ai.openai.api.OpenAiApi$ChatCompletion] and content type [application/json]] with root cause

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "prompt_tokens_details" (class org.springframework.ai.openai.api.OpenAiApi$Usage), not marked as ignorable (4 known properties: "completion_tokens_details", "completion_tokens", "prompt_tokens", "total_tokens"])
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 28, column: 4] (through reference chain: org.springframework.ai.openai.api.OpenAiApi$ChatCompletion["usage"]->org.springframework.ai.openai.api.OpenAiApi$Usage["prompt_tokens_details"])

Environment

Steps to Reproduce

  1. Register the ObjectMapper bean directly as follows:

    @Configuration
    public class SpringConfig {
    
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
    }
  2. A RestClientException occurs when calling the OpenAI API using the Spring AI library.
  3. The error does not occur when using the default ObjectMapper bean provided by Spring.

Expected Behavior

The API call should work without any errors, even when using custom ObjectMapper bean. Or maybe you need to fill in some related information.

Minimal Complete Reproducible Example

AiController

@RestController
public class ChatController {

    private final OpenAiChatModel chatModel;

    @Autowired
    public ChatController(OpenAiChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @GetMapping("/ai/generate")
    public Map<String,String> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        return Map.of("generation", chatModel.call(message));
    }
}

SpringConfig

@Configuration
public class SpringConfig {

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

dafriz commented 4 days ago

The Jackson library class ObjectMapper is configured to fail on unknown properties by default.

If you want to customise an ObjectMapper but still get the config that Spring web configures ( such as disabling this flag ) you can use:

    @Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder objectMapperBuilder) {
       // customise here
        return objectMapperBuilder.build();
    }

If you are using Spring Boot and want to disable failing on unknown properties for all ObjectMappers created this way you can use a Customizer

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> builder.failOnUnknownProperties(false);
    }

It has been suggested ( https://github.com/spring-projects/spring-ai/issues/1369 ) Spring AI should add a @JsonIgnoreProperties(ignoreUnknown = true) to the incoming JSON DTO classes so these errors don't occur as new fields are added by OpenAI and others.

Perhaps sometimes we want to fail on unknown properties - for example to identify any unmapped / new fields, which would be difficult if disabled in each class.

yeseong0412 commented 4 days ago

@dafriz thx for reply

In the project, we decided to use the default ObjectMapper, but we encountered an original problem.

Instead of using @JsonIgnoreProperties(ignoreUnknown = true), I believe it would be more appropriate to add the missing field prompt_tokens_details to the relevant class. (I think ignoring unknown properties can sometimes obscure important changes in the API)

However, I couldn't find any reference to prompt_tokens_details in the OpenAI API documentation, making it unclear whether this is a newly added field or specific to certain requests.

What can we do in this situation?

dafriz commented 4 days ago

np,

I raised a PR the other day to add the prompt_tokens_details - https://github.com/spring-projects/spring-ai/pull/1516 it contains a nested cached_tokens usage metric.

OpenAI API docs are here - https://platform.openai.com/docs/api-reference/chat/object - expand the usage object to see the new nested fields.

yeseong0412 commented 4 days ago

I couldn't check it because it was hidden in the usage.

Good luck!