Skip to content

Commit c328ef6

Browse files
committed
Add docs for metadata support in ChatClient
Signed-off-by: Soby Chacko <[email protected]>
1 parent e547661 commit c328ef6

File tree

1 file changed

+102
-1
lines changed

1 file changed

+102
-1
lines changed

spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chatclient.adoc

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,107 @@ After specifying the `stream()` method on `ChatClient`, there are a few options
379379
* `Flux<ChatResponse> chatResponse()`: Returns a `Flux` of the `ChatResponse` object, which contains additional metadata about the response.
380380
* `Flux<ChatClientResponse> chatClientResponse()`: returns a `Flux` of the `ChatClientResponse` object that contains the `ChatResponse` object and the ChatClient execution context, giving you access to additional data used during the execution of advisors (e.g. the relevant documents retrieved in a RAG flow).
381381

382+
== Message Metadata
383+
384+
The ChatClient supports adding metadata to both user and system messages.
385+
Metadata provides additional context and information about messages that can be used by the AI model or downstream processing.
386+
387+
=== Adding Metadata to User Messages
388+
389+
You can add metadata to user messages using the `metadata()` methods:
390+
391+
[source,java]
392+
----
393+
// Adding individual metadata key-value pairs
394+
String response = chatClient.prompt()
395+
.user(u -> u.text("What's the weather like?")
396+
.metadata("messageId", "msg-123")
397+
.metadata("userId", "user-456")
398+
.metadata("priority", "high"))
399+
.call()
400+
.content();
401+
402+
// Adding multiple metadata entries at once
403+
Map<String, Object> userMetadata = Map.of(
404+
"messageId", "msg-123",
405+
"userId", "user-456",
406+
"timestamp", System.currentTimeMillis()
407+
);
408+
409+
String response = chatClient.prompt()
410+
.user(u -> u.text("What's the weather like?")
411+
.metadata(userMetadata))
412+
.call()
413+
.content();
414+
----
415+
416+
=== Adding Metadata to System Messages
417+
418+
Similarly, you can add metadata to system messages:
419+
420+
[source,java]
421+
----
422+
// Adding metadata to system messages
423+
String response = chatClient.prompt()
424+
.system(s -> s.text("You are a helpful assistant.")
425+
.metadata("version", "1.0")
426+
.metadata("model", "gpt-4"))
427+
.user("Tell me a joke")
428+
.call()
429+
.content();
430+
----
431+
432+
=== Default Metadata Support
433+
434+
You can also configure default metadata at the ChatClient builder level:
435+
436+
[source,java]
437+
----
438+
@Configuration
439+
class Config {
440+
@Bean
441+
ChatClient chatClient(ChatClient.Builder builder) {
442+
return builder
443+
.defaultSystem(s -> s.text("You are a helpful assistant")
444+
.metadata("assistantType", "general")
445+
.metadata("version", "1.0"))
446+
.defaultUser(u -> u.text("Default user context")
447+
.metadata("sessionId", "default-session"))
448+
.build();
449+
}
450+
}
451+
----
452+
453+
=== Metadata Validation
454+
455+
The ChatClient validates metadata to ensure data integrity:
456+
457+
* Metadata keys cannot be null or empty
458+
* Metadata values cannot be null
459+
* When passing a Map, neither keys nor values can contain null elements
460+
461+
[source,java]
462+
----
463+
// This will throw an IllegalArgumentException
464+
chatClient.prompt()
465+
.user(u -> u.text("Hello")
466+
.metadata(null, "value")) // Invalid: null key
467+
.call()
468+
.content();
469+
470+
// This will also throw an IllegalArgumentException
471+
chatClient.prompt()
472+
.user(u -> u.text("Hello")
473+
.metadata("key", null)) // Invalid: null value
474+
.call()
475+
.content();
476+
----
477+
478+
=== Accessing Metadata
479+
480+
The metadata is included in the generated UserMessage and SystemMessage objects and can be accessed through the message's `getMetadata()` method.
481+
This is particularly useful when processing messages in advisors or when examining the conversation history.
482+
382483
== Using Defaults
383484

384485
Creating a `ChatClient` with a default system text in an `@Configuration` class simplifies runtime code.
@@ -645,4 +746,4 @@ Due to a bug in Spring Boot 3.4, the "spring.http.client.factory=jdk" property m
645746
* Streaming is only supported via the Reactive stack. Imperative applications must include the Reactive stack for this reason (e.g. spring-boot-starter-webflux).
646747
* Non-streaming is only supportive via the Servlet stack. Reactive applications must include the Servlet stack for this reason (e.g. spring-boot-starter-web) and expect some calls to be blocking.
647748
* Tool calling is imperative, leading to blocking workflows. This also results in partial/interrupted Micrometer observations (e.g. the ChatClient spans and the tool calling spans are not connected, with the first one remaining incomplete for that reason).
648-
* The built-in advisors perform blocking operations for standards calls, and non-blocking operations for streaming calls. The Reactor Scheduler used for the advisor streaming calls can be configured via the Builder on each Advisor class.
749+
* The built-in advisors perform blocking operations for standards calls, and non-blocking operations for streaming calls. The Reactor Scheduler used for the advisor streaming calls can be configured via the Builder on each Advisor class.

0 commit comments

Comments
 (0)