Skip to content

Tool calling problem when using Spring AI#1423

Description

@habuma

**Please make sure you read the contribution guide and file the issues in the
right place. **
Contribution guide.

馃敶 Required Information

Describe the Bug:

When using Spring AI for the model and when a tool is used, the tool result isn't sent in the 2nd request to the LLM, resulting in an error.

Steps to Reproduce:
Please provide a numbered list of steps to reproduce the behavior:

  1. Recreate the Hello Time example, but using Spring AI integration and passing the SpringAI object to the agent as the model. The behavior seems consistent across any of Spring AI's model abstractions (I tested with OpenAI, Anthropic, Mistral, and Ollama).
  2. Run the application and ask about the time in any city.

Expected Behavior:

I would expect to be told that the time is 10:30

Observed Behavior:

Instead of being told that the time is 10:30, I get an error indicating that there is an inconsistency between the tool calls and tool responses. The error differs depending on which underlying model abstraction is used. For OpenAI, this is the error:

Caused by: com.openai.errors.BadRequestException: 400: An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: call_hWpFYVJKxctwGganJdgV3uoH
	at com.openai.errors.BadRequestException$Builder.build(BadRequestException.kt:98) ~[openai-java-core-4.39.1.jar:4.39.1]
	at com.openai.core.handlers.ErrorHandler$errorHandler$1.handle(ErrorHandler.kt:54) ~[openai-java-core-4.39.1.jar:4.39.1]
	at com.openai.core.handlers.ErrorHandler$errorHandler$1.handle(ErrorHandler.kt:46) ~[openai-java-core-4.39.1.jar:4.39.1]
	at com.openai.services.blocking.chat.ChatCompletionServiceImpl$WithRawResponseImpl.create(ChatCompletionServiceImpl.kt:141) ~[openai-java-core-4.39.1.jar:4.39.1]
	at com.openai.services.blocking.chat.ChatCompletionServiceImpl.create(ChatCompletionServiceImpl.kt:63) ~[openai-java-core-4.39.1.jar:4.39.1]
	at com.openai.services.blocking.chat.ChatCompletionService.create(ChatCompletionService.kt:64) ~[openai-java-core-4.39.1.jar:4.39.1]
	at org.springframework.ai.openai.OpenAiChatModel.lambda$internalCall$2(OpenAiChatModel.java:217) ~[spring-ai-openai-2.0.0.jar:2.0.0]
	at io.micrometer.observation.Observation.observe(Observation.java:634) ~[micrometer-observation-1.17.0.jar:1.17.0]

For Anthropic, this is the error:

Caused by: com.anthropic.errors.BadRequestException: 400: {"type":"error","error":{"type":"invalid_request_error","message":"messages.2: user messages must have non-empty content"},"request_id":"req_011Cduj6JTe7pohNu8mAPvor"}
	at com.anthropic.errors.BadRequestException$Builder.build(BadRequestException.kt:78) ~[anthropic-java-core-2.40.1.jar:2.40.1]
	at com.anthropic.core.handlers.ErrorHandler$errorHandler$1.handle(ErrorHandler.kt:45) ~[anthropic-java-core-2.40.1.jar:2.40.1]
	at com.anthropic.core.handlers.ErrorHandler$errorHandler$1.handle(ErrorHandler.kt:37) ~[anthropic-java-core-2.40.1.jar:2.40.1]
	at com.anthropic.services.blocking.MessageServiceImpl$WithRawResponseImpl.create(MessageServiceImpl.kt:111) ~[anthropic-java-core-2.40.1.jar:2.40.1]
	at com.anthropic.services.blocking.MessageService$WithRawResponse.create(MessageService.kt:154) ~[anthropic-java-core-2.40.1.jar:2.40.1]
	at org.springframework.ai.anthropic.AnthropicChatModel.lambda$internalCall$14(AnthropicChatModel.java:547) ~[spring-ai-anthropic-2.0.0.jar:2.0.0]
	at io.micrometer.observation.Observation.observe(Observation.java:634) ~[micrometer-observation-1.17.0.jar:1.17.0]
	at org.springframework.ai.anthropic.AnthropicChatModel.internalCall(AnthropicChatModel.java:543) ~[spring-ai-anthropic-2.0.0.jar:2.0.0]

For Mistral, this is the error:

Caused by: org.springframework.ai.retry.NonTransientAiException: HTTP 400 - {"object":"error","message":"Not the same number of function calls and responses","type":"invalid_request_message_order","param":null,"code":"3230","raw_status_code":400}
	at org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration$2.handleError(SpringAiRetryAutoConfiguration.java:125) ~[spring-ai-autoconfigure-retry-2.0.0.jar:2.0.0]
	at org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration$2.handleError(SpringAiRetryAutoConfiguration.java:102) ~[spring-ai-autoconfigure-retry-2.0.0.jar:2.0.0]
	at org.springframework.web.client.StatusHandler.lambda$fromErrorHandler$0(StatusHandler.java:98) ~[spring-web-7.0.8.jar:7.0.8]
	at org.springframework.web.client.StatusHandler.handle(StatusHandler.java:75) ~[spring-web-7.0.8.jar:7.0.8]
	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.applyStatusHandlers(DefaultRestClient.java:943) ~[spring-web-7.0.8.jar:7.0.8]
	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.lambda$readBody$0(DefaultRestClient.java:932) ~[spring-web-7.0.8.jar:7.0.8]
	at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:224) ~[spring-web-7.0.8.jar:7.0.8]
	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:931) ~[spring-web-7.0.8.jar:7.0.8]

Ollama is a bit more peculiar, but seems to be somewhat the same problem. Instead of getting an error, the response includes the tool parameters:

To get the current time in Denver, I would use the 'getCurrentTime' tool and provide it with the city name "Denver". Let me check that for you:

[{"name": "getCurrentTime", "arguments": {"city":"Denver"}}]

Environment Details:

  • ADK Library Version: 1.7.1
  • Spring AI version: 2.0.0
  • OS: MacOS

Model Information:

  • In the cases of OpenAI, Anthropic, and Mistral, it is the default models in the Spring AI model abstraction dependencies.
  • In the case of Ollama, I was using mistral:latest

馃煛 Optional Information

Providing this information greatly speeds up the resolution process.

Regression:
Did this work in a previous version of ADK? (Yes/No) If so, which one?

I do not know. I did try this with older versions and Spring Boot 1.1.8, but saw a different set of problems and reverted to SB 2.0.0 and ADK 1.7.1.

Logs:
The relevant logs are pasted above.

Minimal Reproduction Code:
I have this bean configured:

  @Bean
  public BaseAgent initAgent(SpringAI springAi) {
    return LlmAgent.builder()
        .name("hello-time-agent")
        .description("Tells the current time in a specified city")
        .instruction("""
                You are a helpful assistant that tells the current time in a city.
                Use the 'getCurrentTime' tool for this purpose.
                """)
        .model(springAi)
        .tools(FunctionTool.create(HelloTimeAgent.class, "getCurrentTime"))
        .build();
  }

Which relies on the ADK's Spring AI auto-confuration to give the SpringAI object. Because of an ordering issue with that auto-configuration, I had to create the following bridge configuration to force the ChatModel to be created before trying to auto-configure the SpringAI object (which is itself a separate issue):

@AutoConfiguration
@AutoConfigureAfter(OllamaChatAutoConfiguration.class)
@AutoConfigureBefore(SpringAIAutoConfiguration.class)
public class SpringAIAutoConfigurationBridge {
}

Everything else about the project I'm trying this in is the same as the Java implementation of the Hello Time agent.

How often has this issue occurred?:

  • Always (100%)

Metadata

Metadata

Assignees

Labels

waiting on reporterWaiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions