Bug description When there should be multiple function calls for a single question, only one call is made because AzureOpenAiChatClient doesn't copy tools value from first request to second request, as a result, the next function call will not happen (loosing context in the function call response).

Environment spring-ai-azure-openai-1.0.0-20240420.063719-112.jar

Steps to reproduce - AzureOpenAiChatClient makes a call to gpt-4 with question "What's the weather like in San Francisco, Tokyo, and Paris?" and function "weatherFunction" - AzureOpenAiChatClient receives a function call in response for "San Francisco" - AzureOpenAiChatClient calls the function and builds a second request including the result, sends new request - AzureOpenAiChatClient receives the result for "San Francisco" and stops

Expected behavior Multiple function calls should happen (for SanFrancisco, Tokyo, Paris).

Analysis This issue doesn't happen if using OpenAIChatClient. And comparing OpenAiChatClient.doCreateToolResponseRequest and AzureOpenAiChatClient.doCreateToolResponseRequest shows that OpenAiChatClient uses ModelOptionsUtils.merge(newRequest, previousRequest, ChatCompletionRequest.class) to copy attributes (including tools) from first request to second request, while AzureOpenAiChatClient uses AzureOpenAiChatClient.merge to copy attributes (missing tools). So it looks like missing tools property is causing the issue.

Minimal Complete Reproducible example

   // Kotlin code snippet
    val response: ChatResponse = chatClient.call(
        Prompt(
            listOf(UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?")),
            AzureOpenAiChatOptions.builder().withFunction("weatherFunction").build()
        )
    )

    println(response.result.output.content)
Getting weather for San Francisco in C
The current temperature in San Francisco is 30 degrees Celsius. 

Now let's check the weather in Tokyo and Paris. 

Comment From: tzolov

@tha2015 our integration tests for parallel function calling are successful: https://github.com/spring-projects/spring-ai/tree/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool

Mind that you need to deploy a mode gpt-4-0125-preview or new.

Comment From: tha2015

Thanks Christian. I could not set up spring-ai project to run the tests (there were some compile errors) and also I don't have an Azure key to test it. Actually I'm using AzureOpenAiChatClient with some custom logic for my environment so flows might be a bit different and I can't explain why the IT tests work and my logic doesn't. But I still suspect that AzureOpenAiChatClient.doCreateToolResponseRequest has some issue because it doesn't copy tools from the previous request to the next request. Can you have a look when you have some time? Thanks.

Comment From: tha2015

@tzolov I believe that my model is not latest so it doesn't support Parallel function calling. As a result, it requires 3 round trips to do 3 function calls (instead of one). And your test seems to work with newer model and just requires one round trip to do 3 function calls. When there are multiple round trips, the tools value must be copied from first request to the following requests but it didn't happen. Can you review logic of AzureOpenAiChatClient.doCreateToolResponseRequest to see why it doesn't copy tools property? Thanks.

Comment From: avani17101

@tzolov I am facing this for gpt4o too. I assume it is definitely newer than gpt-4-0125-preview

Comment From: timostark

@tha2015 @avani17101 raised a PR https://github.com/spring-projects/spring-ai/pull/1042 .. let's see if it can get merged that way.