Sections in this lesson
Spring AI ChatClient
Compose versioned requests, inspect response metadata and stream text through an injected ChatClient builder.
Reviewed: · Content owner: Interview Preparator
What you will learn
- Keep provider execution behind an injected builder.
- Apply prompt parameters without turning user data into authority.
- Handle full responses and streaming as different interfaces.
Before you start
The flow
- 1.Validate topic
- 2.Apply template
- 3.Call or stream
- 4.Inspect result
- 5.Verify
Use the configured builder
A Boot application can inject the auto-configured ChatClient.Builder so registered customization and observation are retained. A plain unit test supplies ChatClient.builder(fakeModel). Keeping the service constructor explicit makes those two compositions visible.
The standalone 1.0.3 pilot remains in examples/spring-ai-chat-client as a historical comparison. This lesson now uses the Phase 2 laboratory: Java 21, Boot 4.0.5 and Spring AI 2.0.2. Do not merge the two Maven configurations.
Templates and response metadata
The example keeps system instructions separate and inserts a validated topic into a user-message template. Parameters avoid hand-built template concatenation; they do not make untrusted text safe or replace authorization.
chatResponse() preserves model response metadata alongside text. Provider support determines which usage and timing fields are available. Record actual usage when supplied; do not infer billable tokens from character count or assume every provider returns the same metadata.
Streaming changes completion handling
stream().content() exposes text chunks; call().entity() needs a complete response for typed conversion. A production SSE endpoint must handle cancellation, disconnects, backpressure and errors after headers are sent. The example returns Flux and does not pretend to implement the full HTTP streaming layer.
Tests assert the actual rendered prompt, response model metadata and ordered chunks from a synthetic streaming model. These are integration contracts, not live model quality or latency benchmarks.
Try it
package com.interviewpreparator.lab;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.model.ChatResponse;import reactor.core.publisher.Flux;public final class ChatClientExample { private final ChatClient client; public ChatClientExample(ChatClient.Builder builder) { this.client = builder.defaultSystem("Explain Java briefly. State uncertainty when evidence is missing.").build(); } public ChatResponse explain(String topic) { validate(topic); return client.prompt().user(user -> user.text("Explain {topic}").param("topic", topic)) .call().chatResponse(); } public Flux<String> stream(String topic) { validate(topic); return client.prompt().user(user -> user.text("Explain {topic}").param("topic", topic)) .stream().content(); } private static void validate(String topic) { if (topic == null || topic.isBlank() || topic.length() > 200) { throw new IllegalArgumentException("Topic must contain 1–200 characters"); } }}examples/spring-ai-lab/src/main/java/com/interviewpreparator/lab/ChatClientExample.java · mvn -f examples/spring-ai-lab/pom.xml test. Java 21+, Spring Boot 4.0.5, Spring AI 2.0.2 where applicable. The demo uses synthetic models; live inference quality is a separate check.
Interview questions
Why inject the Boot builder?
To preserve configured customization and observation while keeping provider dependencies replaceable in tests.
Can typed entity parsing happen on partial text chunks?
The complete entity path requires a full response; incremental presentation and final validation are separate concerns.
Does a template parameter prevent prompt injection?
No. It composes data safely as a template value, but model behavior and application permissions still need controls.
Practice
Add a stream that emits one chunk and then an error; define what the caller may display and persist.
Check your solution
- Invalid input is rejected before model execution.
- Prompt text and metadata are verified.
- A failed partial stream is not recorded as a complete answer.
Official references
Continue learning
Category sequence
Spring AI
Lesson 2 / 7
Was this lesson useful?
Change notes: Phase 2: bilingual lesson with versioned executable reference.