Building AI Applications with Spring AI (2): Implementing Chat Histories and Instant SSE Responses
Following our initial dive into creating prompts with Spring AI, this article ventures further into enhancing user interactions. We focus on incorporating chat histories and delivering responses in real-time using Server-Sent Events (SSE). This combination not only elevates the user experience by providing instant feedback but also simulates a dynamic conversation flow, akin to real-life interactions.

Why Chat Histories and Real-Time Responses are Crucial
Context is paramount in any conversation, especially when it comes to AI generating relevant and coherent responses. By integrating chat histories, AI can consider previous exchanges, offering a more personalized and engaging user experience. Combined with SSE for real-time messaging, our chat application becomes lively and interactive, ensuring users remain engaged by receiving immediate responses.
Implementing Chat Histories
First, we need to store chat messages which will later be used to provide context for AI-generated responses. Although our example utilizes a simple in-memory solution for demonstration, a database implementation is advisable for production scenarios.
1 |
|
The ChatHistoryService
This service is responsible for storing and retrieving chat messages:
1 | import org.springframework.ai.chat.messages.Message; |
Generating Contextual Responses
For the AI to generate relevant responses, the chat history must be included to provide context:
1 | public Prompt createPromptWithHistory(String userInput) { |
This process ensures every prompt sent to the AI encompasses the entire conversation history, enabling the generation of more relevant and engaging responses.
Implementing Real-Time Responses with SSE
To offer immediate feedback to users, we leverage SSE. This entails modifying the ChatController to include chat history in the prompt and use SseEmitter for real-time communication.
The ChatController
The ChatController is a Spring MVC controller that handles HTTP GET requests to the /chat-stream endpoint. It’s designed to initiate a real-time chat session between the user and an AI model using Server-Sent Events (SSE) for live message streaming. Additionally, it incorporates a chat history feature to provide context to the AI, enhancing the relevance of its responses.
1 |
|
Breaking Down the Code
1 |
@Slf4j: This Lombok annotation automatically generates aLoggerobject, allowing us to log messages throughout the class.@RestController: Marks this class as a controller with methods that return domain objects rather than views. It’s a convenience annotation combining@Controllerand@ResponseBody.@RequiredArgsConstructor: Another Lombok annotation that generates a constructor with 1 parameter for each field that requires special handling. All non-initializedfinalfields get a parameter, as well as any@NonNullfields.@FieldDefaults(level = AccessLevel.PRIVATE): Sets the access level of class fields. Here, it makes all fields private by default, enhancing encapsulation.
1 | final ChatHistoryService chatHistoryService; |
- These are dependencies injected by Spring’s Inversion of Control (IoC) container.
ChatHistoryServicemanages chat history, andOpenAiChatClientinterfaces with OpenAI’s chat API.
1 |
|
- This method is mapped to handle HTTP GET requests to
/chat-stream. It accepts a query parameter namedinput, which represents the user’s message to the AI.
1 | final SseEmitter emitter = new SseEmitter(120 * 1000L); |
- An
SseEmitterinstance is created with a timeout of 120 seconds. It’s used to send server-sent events to the client, facilitating real-time communication.
1 | Prompt prompt = createPromptWithHistory(input); |
- A
Promptobject is created with the user’s input and any existing chat history. This context allows the AI to generate more relevant responses.
1 | Flux<ChatResponse> chatResponseFlux = openAiChatClient.stream(prompt); |
- The
openAiChatClient.stream(prompt)call initiates the conversation with the AI, returning aFlux<ChatResponse>- a reactive stream ofChatResponseobjects representing the AI’s replies.
1 | chatResponseFlux.doOnNext(chatResponse -> { |
- For each
ChatResponsereceived (doOnNext), its content is appended to aStringBuilderto accumulate the full conversation. If the content is not empty (StringUtils.hasText), it’s then sent to the client usingemitter.send().
1 | .doOnError(emitter::completeWithError) |
- If an error occurs in the stream (
doOnError), it’s propagated to the client by completing theSseEmitterwith an error.
1 | .doOnComplete(() -> { |
- Once all messages have been processed (
doOnComplete), the user’s input and the full AI response are added to the chat history. The log captures the AI’s full response for debugging or auditing purposes. Finally, theSseEmitteris marked as complete, and the reactive stream is subscribed to, activating the sequence of operations.
1 | return emitter; |
- The method returns the
SseEmitterto the client, enabling real-time streaming of chat messages.
Testing Continuity with cURL Commands
To effectively test the continuity of the conversation, utilize the following cURL commands:
First Test Call:
1 | curl --location '127.0.0.1:8080/chat-stream?input=Who%20is%20the%20bad%20guy%20in%20Kamen%20Rider%3F' |
Second Test Call:
1 | curl --location '127.0.0.1:8080/chat-stream?input=What%20is%20their%20mission%3F' |
These commands simulate a continuous conversation flow, showcasing the application’s capability to maintain context and deliver real-time responses.
Conclusion
By embedding chat histories and leveraging SSE for instant responses, we have significantly amplified the capabilities of our Spring AI-powered application. This approach not only renders interactions more engaging and personalized but also sets the stage for advanced features like handling multimodal inputs or integrating external APIs for expanded functionalities. As you continue to explore the vast possibilities with Spring AI, let your imagination lead the way to innovation. Happy coding!
Stay tuned for more tutorials in this series as we delve deeper into the exciting world of AI application development with Spring AI.
References and Future Tools
As the Spring AI project evolves, new tools and libraries are anticipated to simplify aspects like chat history management. For instance, a discussion on GitHub hints at the development of an official Spring AI ChatHistory abstraction. This will offer a more streamlined approach to incorporating chat histories into AI interactions, allowing for even more sophisticated chat applications.