Mastering Spring AI ChatClient: call vs. stream for typewriter‑like streaming
This article explains how Spring AI's ChatClient serves as a high‑level façade over ChatModel, compares the synchronous call() method with the streaming stream() method, demonstrates retrieving plain text, full responses, or structured entities, and shows how to override model parameters such as temperature on a per‑request basis.
ChatModel and ChatClient
Spring AI defines two API layers. ChatModel is the low‑level engine (e.g., DeepSeekChatModel) that exposes call(Prompt) and returns a ChatResponse. ChatClient is a high‑level fluent façade that wraps a ChatModel and adds prompt templates, advisors, structured output, and streaming capabilities.
Position : ChatModel – low‑level engine; ChatClient – high‑level façade (recommended for daily use).
Invocation : ChatModel – call(Prompt); ChatClient – prompt().user(...).call().
Template/Advisor support : none in ChatModel; built‑in in ChatClient.
Structured output : manual handling with ChatModel; single‑line .entity() in ChatClient.
Obtaining a ChatClient
// Preferred: inject the auto‑configured Builder (adds default advisors)
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
// Simple scenario: create directly from a ChatModel
ChatClient client = ChatClient.create(chatModel);call() – synchronous return variants
The call() method can return results at different granularities.
// ① Only the reply text
String text = chatClient.prompt().user("Tell me a joke").call().content();
// ② Full response including token usage and metadata
ChatResponse resp = chatClient.prompt().user("Tell me a joke").call().chatResponse();
String reply = resp.getResult().getOutput().getText(); // reply text
Usage usage = resp.getMetadata().getUsage(); // token usage
System.out.println("Tokens used: " + usage.getTotalTokens()); .content()– returns String containing only the reply text. .chatResponse() – returns ChatResponse with token usage, multiple candidates, and metadata. .entity(Class) – returns a Java object for structured output (covered in a later article).
When using hosted large models that charge per token, the Usage object inside ChatResponse provides the basis for cost accounting.
stream() – streaming output like a typewriter
Replacing call() with stream() returns a Flux that emits each generated chunk, allowing immediate character‑by‑character display.
@GetMapping(value = "/ai/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String message) {
// Flux<String> pushes each small segment; combine with SSE on the front end
return chatClient.prompt(message).stream().content();
}Return type : call() → String or ChatResponse; stream() → Flux<String> or Flux<ChatResponse>.
Timing : call() returns after the entire answer is generated; stream() pushes partial results while generating.
Typical scenario : call() for background tasks needing the full result; stream() for chat UI or long text where incremental display is desired.
Front‑end code can use EventSource to receive the SSE stream and achieve the classic typewriter effect of chat bots.
Overriding parameters for a single request
The default temperature is configured in application.properties. To obtain a more deterministic response for a specific request (e.g., code generation), override it with .options() without changing the global setting.
String code = chatClient.prompt()
.user("Write a thread‑safe singleton in Java")
// Override temperature to 0.2 for this call only
.options(ChatOptions.builder().temperature(0.2).build())
.call()
.content();Setting a global default and overriding per request is the common pattern for handling model parameters in Spring AI.
Day 2 Summary
ChatModel – low‑level engine; invoke with call(Prompt).
ChatClient – high‑level fluent façade; preferred for everyday use. .content() – extracts reply text. .chatResponse() – obtains full response including token usage. stream().content() – returns Flux<String> for streaming output. .options() – overrides model parameters for a single request.
Related links
ChatClient API: https://docs.spring.io/spring-ai/reference/api/chatclient.html<br/>ChatModel API: https://docs.spring.io/spring-ai/reference/api/chatmodel.html<br/>DeepSeek integration docs: https://docs.spring.io/spring-ai/reference/api/chat/deepseek-chat.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tech Ocean
Focused on AI programming, sharing ready-to-use development efficiency solutions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
