CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-ai--spring-ai-client-chat

Spring AI Chat Client provides a fluent API for building AI-powered applications with LLMs, supporting advisors, streaming, structured outputs, and conversation memory

Overview
Eval results
Files

observability.mddocs/reference/

Observability

Spring AI Chat Client integrates with Micrometer for comprehensive observability through metrics, traces, and logs. This enables monitoring of chat interactions, advisor performance, and request/response flows.

Imports

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.observation.ChatClientObservationContext;
import org.springframework.ai.chat.client.observation.ChatClientObservationConvention;
import org.springframework.ai.chat.client.observation.DefaultChatClientObservationConvention;
import org.springframework.ai.chat.client.observation.ChatClientObservationDocumentation;
import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationHandler;
import org.springframework.ai.chat.client.observation.ChatClientCompletionObservationHandler;
import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationContext;
import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationConvention;
import org.springframework.ai.chat.client.advisor.observation.DefaultAdvisorObservationConvention;
import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationDocumentation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.Observation;

Chat Client Observations

Chat Client creates observations for each request, recording metrics and traces.

Enabling Observations

Configure ChatClient with an ObservationRegistry to enable observations.

import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;

// Create with observation registry
ObservationRegistry registry = ObservationRegistry.create();

ChatClient client = ChatClient.create(chatModel, registry);

// Or with builder
ChatClient client = ChatClient.builder(chatModel)
    .observationRegistry(registry)
    .build();

Chat Client Observation Context

The observation context captures request and response metadata.

class ChatClientObservationContext extends Observation.Context {
    static Builder builder();

    ChatClientRequest getRequest();
    ChatClientResponse getResponse();
    void setResponse(ChatClientResponse response);
    AiOperationMetadata getOperationMetadata();
    List<Advisor> getAdvisors();
    boolean isStream();
    String getFormat();

    interface Builder {
        Builder request(ChatClientRequest request);
        Builder format(String format);
        Builder advisors(List<Advisor> advisors);
        Builder stream(boolean stream);
        ChatClientObservationContext build();
    }
}

Fields:

  • request - The ChatClientRequest being executed
  • response - The ChatClientResponse (set after execution)
  • operationMetadata - AI operation metadata
  • advisors - List of advisors in the chain
  • stream - Whether this is a streaming request
  • format - Output format from context

Observation Tags

Chat Client observations include low and high cardinality tags.

enum ChatClientObservationDocumentation implements ObservationDocumentation {
    AI_CHAT_CLIENT;

    enum LowCardinalityKeyNames implements KeyName {
        SPRING_AI_KIND("spring.ai.kind"),
        STREAM("spring.ai.chat.client.stream");

        private final String key;
        String asString();
    }

    enum HighCardinalityKeyNames implements KeyName {
        CHAT_CLIENT_ADVISORS("spring.ai.chat.client.advisors"),
        CHAT_CLIENT_CONVERSATION_ID("spring.ai.chat.client.conversation.id"),
        CHAT_CLIENT_TOOL_NAMES("spring.ai.chat.client.tool.names");

        private final String key;
        String asString();
    }
}

Low Cardinality Tags (limited distinct values):

  • spring.ai.kind - Component type (always "chat.client")
  • spring.ai.chat.client.stream - Streaming flag (true/false)

High Cardinality Tags (many distinct values):

  • spring.ai.chat.client.advisors - Comma-separated advisor names
  • spring.ai.chat.client.conversation.id - Conversation identifier
  • spring.ai.chat.client.tool.names - Comma-separated tool names

Custom Observation Convention

Customize observation names and tags with a custom convention.

interface ChatClientObservationConvention
    extends ObservationConvention<ChatClientObservationContext> {
}

class DefaultChatClientObservationConvention
    implements ChatClientObservationConvention {

    static final String DEFAULT_NAME = "spring.ai.chat.client";

    DefaultChatClientObservationConvention();
    DefaultChatClientObservationConvention(String name);
}

Example:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.observation.ChatClientObservationConvention;
import org.springframework.ai.chat.client.observation.ChatClientObservationContext;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.common.KeyValues;

// Custom convention
class CustomChatClientConvention implements ChatClientObservationConvention {
    @Override
    public String getName() {
        return "my.app.chat.client";
    }

    @Override
    public KeyValues getLowCardinalityKeyValues(
        ChatClientObservationContext context
    ) {
        return KeyValues.of(
            "app.name", "my-app",
            "stream", String.valueOf(context.isStream())
        );
    }

    @Override
    public KeyValues getHighCardinalityKeyValues(
        ChatClientObservationContext context
    ) {
        return KeyValues.of(
            "request.format", context.getFormat() != null ?
                context.getFormat() : "text"
        );
    }
}

// Configure client
ChatClient client = ChatClient.builder(chatModel)
    .observationRegistry(registry)
    .chatClientObservationConvention(new CustomChatClientConvention())
    .build();

Advisor Observations

Each advisor in the chain creates its own observation, enabling fine-grained monitoring.

Advisor Observation Context

class AdvisorObservationContext extends Observation.Context {
    static Builder builder();

    String getAdvisorName();
    void setAdvisorName(String advisorName);
    ChatClientRequest getChatClientRequest();
    void setChatClientRequest(ChatClientRequest request);
    int getOrder();
    void setOrder(int order);
    ChatClientResponse getChatClientResponse();
    void setChatClientResponse(ChatClientResponse response);

    interface Builder {
        Builder advisorName(String advisorName);
        Builder chatClientRequest(ChatClientRequest request);
        Builder order(int order);
        AdvisorObservationContext build();
    }
}

Fields:

  • advisorName - Name of the advisor
  • chatClientRequest - Request being processed
  • order - Advisor order in chain
  • chatClientResponse - Response (set after execution)

Advisor Observation Tags

enum AdvisorObservationDocumentation implements ObservationDocumentation {
    AI_ADVISOR;

    enum LowCardinalityKeyNames implements KeyName {
        AI_OPERATION_TYPE("spring.ai.kind"),
        AI_PROVIDER("spring.ai.ai-provider"),
        SPRING_AI_KIND("spring.ai.kind"),
        ADVISOR_NAME("spring.ai.advisor.name");

        private final String key;
        String asString();
    }

    enum HighCardinalityKeyNames implements KeyName {
        ADVISOR_ORDER("spring.ai.advisor.order");

        private final String key;
        String asString();
    }
}

Low Cardinality Tags:

  • spring.ai.kind - Component type (always "advisor")
  • spring.ai.ai-provider - AI provider name
  • spring.ai.advisor.name - Advisor name

High Cardinality Tags:

  • spring.ai.advisor.order - Advisor order in chain

Custom Advisor Convention

interface AdvisorObservationConvention
    extends ObservationConvention<AdvisorObservationContext> {
}

class DefaultAdvisorObservationConvention
    implements AdvisorObservationConvention {

    static final String DEFAULT_NAME = "spring.ai.advisor";

    DefaultAdvisorObservationConvention();
    DefaultAdvisorObservationConvention(String name);
}

Example:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationConvention;
import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationContext;
import io.micrometer.common.KeyValues;

class CustomAdvisorConvention implements AdvisorObservationConvention {
    @Override
    public String getName() {
        return "my.app.advisor";
    }

    @Override
    public KeyValues getLowCardinalityKeyValues(
        AdvisorObservationContext context
    ) {
        return KeyValues.of(
            "advisor.name", context.getAdvisorName(),
            "app.component", "chat-client"
        );
    }

    @Override
    public KeyValues getHighCardinalityKeyValues(
        AdvisorObservationContext context
    ) {
        return KeyValues.of(
            "advisor.order", String.valueOf(context.getOrder())
        );
    }
}

// Configure client
ChatClient client = ChatClient.builder(chatModel)
    .observationRegistry(registry)
    .advisorObservationConvention(new CustomAdvisorConvention())
    .build();

Observation Handlers

Custom handlers can process observations for logging or custom metrics.

Chat Client Prompt Handler

Logs prompt content when observations complete.

class ChatClientPromptContentObservationHandler
    implements ObservationHandler<ChatClientObservationContext> {

    void onStop(ChatClientObservationContext context);
    boolean supportsContext(Observation.Context context);
}

Example:

import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationHandler;
import io.micrometer.observation.ObservationRegistry;

ObservationRegistry registry = ObservationRegistry.create();

// Register handler
registry.observationConfig()
    .observationHandler(
        new ChatClientPromptContentObservationHandler()
    );

ChatClient client = ChatClient.builder(chatModel)
    .observationRegistry(registry)
    .build();

Chat Client Completion Handler

Logs completion content when observations complete.

class ChatClientCompletionObservationHandler
    implements ObservationHandler<ChatClientObservationContext> {

    void onStop(ChatClientObservationContext context);
    boolean supportsContext(Observation.Context context);
}

Example:

import org.springframework.ai.chat.client.observation.ChatClientCompletionObservationHandler;
import io.micrometer.observation.ObservationRegistry;

ObservationRegistry registry = ObservationRegistry.create();

// Register handler
registry.observationConfig()
    .observationHandler(
        new ChatClientCompletionObservationHandler()
    );

Custom Observation Handler

Create custom handlers for specialized processing.

import org.springframework.ai.chat.client.observation.ChatClientObservationContext;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;

class MetricsCollectionHandler
    implements ObservationHandler<ChatClientObservationContext> {

    private final MetricsService metricsService;

    public MetricsCollectionHandler(MetricsService metricsService) {
        this.metricsService = metricsService;
    }

    @Override
    public void onStart(ChatClientObservationContext context) {
        // Record request start
        metricsService.recordRequestStart(
            context.getRequest(),
            context.isStream()
        );
    }

    @Override
    public void onStop(ChatClientObservationContext context) {
        // Record completion
        metricsService.recordRequestComplete(
            context.getRequest(),
            context.getResponse(),
            context.isStream()
        );
    }

    @Override
    public void onError(ChatClientObservationContext context) {
        // Record error
        metricsService.recordError(
            context.getRequest(),
            context.getError()
        );
    }

    @Override
    public boolean supportsContext(Observation.Context context) {
        return context instanceof ChatClientObservationContext;
    }
}

// Register handler
registry.observationConfig()
    .observationHandler(new MetricsCollectionHandler(metricsService));

Spring Boot Integration

Spring Boot auto-configures observability when Micrometer is on the classpath.

application.properties:

# Enable observations
management.observations.enabled=true

# Enable tracing
management.tracing.enabled=true
management.tracing.sampling.probability=1.0

# Enable metrics
management.metrics.export.prometheus.enabled=true

Example with Spring Boot:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.ChatClientCustomizer;
import org.springframework.ai.chat.model.ChatModel;
import io.micrometer.observation.ObservationRegistry;

@SpringBootApplication
public class ChatApplication {
    public static void main(String[] args) {
        SpringApplication.run(ChatApplication.class, args);
    }

    // ObservationRegistry auto-configured by Spring Boot
    @Bean
    public ChatClient chatClient(
        ChatModel chatModel,
        ObservationRegistry observationRegistry
    ) {
        return ChatClient.builder(chatModel)
            .observationRegistry(observationRegistry)
            .build();
    }

    // Or use customizer
    @Bean
    public ChatClientCustomizer observabilityCustomizer(
        ObservationRegistry observationRegistry
    ) {
        return builder -> builder
            .observationRegistry(observationRegistry);
    }
}

Metrics and Traces

Observations automatically generate metrics and traces when configured.

Example Metrics:

  • spring.ai.chat.client - Timer metric for chat client calls
    • Tags: spring.ai.kind=chat.client, stream=true/false
  • spring.ai.advisor - Timer metric for advisor execution
    • Tags: spring.ai.advisor.name=MessageChatMemoryAdvisor

Example Traces: Each request creates a trace with spans for:

  • Chat client execution
  • Each advisor in chain
  • Underlying ChatModel call

Viewing with Prometheus:

# Request rate
rate(spring_ai_chat_client_seconds_count[5m])

# Request duration (95th percentile)
histogram_quantile(0.95,
  rate(spring_ai_chat_client_seconds_bucket[5m])
)

# Advisor performance
rate(spring_ai_advisor_seconds_count{
  advisor_name="MessageChatMemoryAdvisor"
}[5m])

Complete Example

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationHandler;
import org.springframework.ai.chat.client.observation.ChatClientCompletionObservationHandler;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;

// Set up observability
MeterRegistry meterRegistry = new SimpleMeterRegistry();
ObservationRegistry observationRegistry = ObservationRegistry.create();
observationRegistry.observationConfig()
    .observationHandler(new ChatClientPromptContentObservationHandler())
    .observationHandler(new ChatClientCompletionObservationHandler());

// Create chat client with observability
ChatMemory chatMemory = new InMemoryChatMemory();

ChatClient client = ChatClient.builder(chatModel)
    .observationRegistry(observationRegistry)
    .defaultAdvisors(
        MessageChatMemoryAdvisor.builder(chatMemory)
            .build()
    )
    .build();

// Use client (automatically observed)
String response = client
    .prompt()
    .user("What is Spring AI?")
    .call()
    .content();

// Observations automatically recorded:
// - spring.ai.chat.client (main request)
// - spring.ai.advisor (MessageChatMemoryAdvisor)
// - spring.ai.advisor (ChatModelCallAdvisor)

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-ai--spring-ai-client-chat

docs

index.md

tile.json