Bug description
Streamed chunks might end up in a wrong order:
data: <A>
data: <B>
can end up as <B> => <A>
Environment
* Spring AI 1.0.1
* Java 21
Steps to reproduce
It's enough to inject the default AzureOpenAIChatModel
, build a ChatClient
and call
val chatClient = ChatClient.builder(chatModel).build()
chatClient
.prompt(createPrompt(simpleResponseDeploymentName))
.stream()
.content()
.collectList()
.block()
On a local machine I need many sequential runs to trigger the issue, thus, a setup with WireMock is advisable.
Expected behavior The response chunks should be ordered correctly.
Minimal Complete Reproducible example Repo with test setup: https://github.com/robinmayerhofer/spring-ai-azure-openai-streaming-issue
Comment From: robinmayerhofer
I have a 99% suspicion that these two flatMap
operations cause this:
* https://github.com/spring-projects/spring-ai/blob/e5e2f45a83c3269126c0af4c7b3b3e32f729b4dd/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java#L354
* https://github.com/spring-projects/spring-ai/blob/e5e2f45a83c3269126c0af4c7b3b3e32f729b4dd/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java#L380
flatMap
does not guarantee ordering and by evaluating the thread and I can see that this runs on boundedElastic
by checking the thread name.
Moreover, AzureOpenAiChatModel
also explicitly uses Schedulers.boundedElastic()
for tool calls:
https://github.com/spring-projects/spring-ai/blob/e5e2f45a83c3269126c0af4c7b3b3e32f729b4dd/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java#L406
Using flatMapSequential
fixes this behavior, at least I cannot trigger it anymore with my setup.
Comment From: robinmayerhofer
The GitHub Action run on my reproduction repo shows that the issue can be triggered: https://github.com/robinmayerhofer/spring-ai-azure-openai-streaming-issue/actions/runs/16973297866/job/48115646716
```
Task :test
SpringAiAzureOpenaiStreamingIssueApplicationTests > tool call response scenario - order should not be messed up(RepetitionInfo) > repetition 21 of 20000 FAILED org.opentest4j.AssertionFailedError at SpringAiAzureOpenaiStreamingIssueApplicationTests.kt:106
SpringAiAzureOpenaiStreamingIssueApplicationTests > simple response - order should not be messed up(RepetitionInfo) > repetition 18863 of 20000 FAILED org.opentest4j.AssertionFailedError at SpringAiAzureOpenaiStreamingIssueApplicationTests.kt:84 ```
PR with the fix: #4156