Please do a quick search on GitHub issues first, there might be already a duplicate issue for the one you are about to create.
If the bug is trivial, just go ahead and create the issue. Otherwise, please take a few moments and fill in the following sections:
Bug description
Spring AI ships ChatModelPromptContentObservationHandler to expose LLM prompt and completion content, but it only emits them via SLF4J logging. It does not attach them as OpenTelemetry span attributes (gen_ai.input.messages / gen_ai.output.messages).
As a result, observability backends that rely on OTEL span data — such as Datadog LLM Observability, Honeycomb, or any OTel-native platform — cannot display the actual prompt and completion content in traces. Users are forced to write and register custom ObservationHandler implementations just to bridge this gap (see workaround below).
The same gap exists for tool/function call inputs and outputs: there is no built-in handler that records these as span attributes.
Spring AI already defines the semantic conventions (gen_ai.*) and wires up the ObservationRegistry, so emitting this data as span tags should be supported out of the box.
Environment
Spring AI: 1.1.x (please confirm which version introduced ChatModelPromptContentObservationHandler)
Java: 21
Tracing: Micrometer Tracing + OpenTelemetry bridge (e.g. micrometer-tracing-bridge-otel)
Observability backend: Datadog (LLM Observability), but affects any OTEL-native backend
Steps to reproduce
Add Spring AI with a chat model (e.g. Anthropic, OpenAI).
Configure Micrometer Tracing with an OTEL exporter.
Enable prompt/completion content observation via spring.ai.chat.observations.include-prompt=true and spring.ai.chat.observations.include-completion=true.
Send a chat request and inspect the exported spans.
Expected behavior
The spans exported to an OTEL backend should include:
gen_ai.input.messages — serialized list of prompt messages with role and content
gen_ai.output.messages — serialized list of completion messages with role and content parts
Tool call inputs/outputs as span attributes when tools are invoked
This aligns with the OpenTelemetry Generative AI semantic conventions.
Actual behavior
ChatModelPromptContentObservationHandler logs prompt/completion content to SLF4J only. No span attributes are set. OTEL backends receive traces with no LLM-specific content.
Minimal Complete Reproducible example
Users currently have to write a custom handler like this to fill the gap:
public class ChatModelSpanContentObservationHandler
implements ObservationHandler {
private final ObjectMapper objectMapper;
@Override
public void onStop(ChatModelObservationContext context) {
TracingObservationHandler.TracingContext tracingContext =
context.get(TracingObservationHandler.TracingContext.class);
if (tracingContext == null) return;
Span span = tracingContext.getSpan();
if (span == null) return;
tagPrompt(span, context);
tagCompletion(span, context);
}
private void tagPrompt(Span span, ChatModelObservationContext context) {
var instructions = context.getRequest().getInstructions();
if (instructions == null || instructions.isEmpty()) return;
try {
List<Map<String, String>> messages = instructions.stream()
.map(msg -> Map.of("role", msg.getMessageType().getValue(),
"content", msg.getText()))
.collect(Collectors.toList());
span.tag("gen_ai.input.messages", objectMapper.writeValueAsString(messages));
} catch (Exception e) {
logger.info("Failed to serialize prompt for span tagging", e);
}
}
// ... tagCompletion similarly
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof ChatModelObservationContext;
}
}
This should not be necessary. Spring AI should either extend ChatModelPromptContentObservationHandler to also tag spans, or provide a dedicated ChatModelSpanContentObservationHandler that is auto-configured when tracing is on the classpath.
Suggested Fix
Extend ChatModelPromptContentObservationHandler#onStop to also call span.tag(...) when a TracingContext is present, similar to the workaround above.
Provide equivalent coverage for tool call observation contexts.
Auto-configure these handlers when micrometer-tracing is on the classpath, guarded by the existing spring.ai.chat.observations.include-prompt / include-completion flags.
Please do a quick search on GitHub issues first, there might be already a duplicate issue for the one you are about to create.
If the bug is trivial, just go ahead and create the issue. Otherwise, please take a few moments and fill in the following sections:
Bug description
Spring AI ships ChatModelPromptContentObservationHandler to expose LLM prompt and completion content, but it only emits them via SLF4J logging. It does not attach them as OpenTelemetry span attributes (gen_ai.input.messages / gen_ai.output.messages).
As a result, observability backends that rely on OTEL span data — such as Datadog LLM Observability, Honeycomb, or any OTel-native platform — cannot display the actual prompt and completion content in traces. Users are forced to write and register custom ObservationHandler implementations just to bridge this gap (see workaround below).
The same gap exists for tool/function call inputs and outputs: there is no built-in handler that records these as span attributes.
Spring AI already defines the semantic conventions (gen_ai.*) and wires up the ObservationRegistry, so emitting this data as span tags should be supported out of the box.
Environment
Spring AI: 1.1.x (please confirm which version introduced ChatModelPromptContentObservationHandler)
Java: 21
Tracing: Micrometer Tracing + OpenTelemetry bridge (e.g. micrometer-tracing-bridge-otel)
Observability backend: Datadog (LLM Observability), but affects any OTEL-native backend
Steps to reproduce
Add Spring AI with a chat model (e.g. Anthropic, OpenAI).
Configure Micrometer Tracing with an OTEL exporter.
Enable prompt/completion content observation via spring.ai.chat.observations.include-prompt=true and spring.ai.chat.observations.include-completion=true.
Send a chat request and inspect the exported spans.
Expected behavior
The spans exported to an OTEL backend should include:
gen_ai.input.messages — serialized list of prompt messages with role and content
gen_ai.output.messages — serialized list of completion messages with role and content parts
Tool call inputs/outputs as span attributes when tools are invoked
This aligns with the OpenTelemetry Generative AI semantic conventions.
Actual behavior
ChatModelPromptContentObservationHandler logs prompt/completion content to SLF4J only. No span attributes are set. OTEL backends receive traces with no LLM-specific content.
Minimal Complete Reproducible example
Users currently have to write a custom handler like this to fill the gap:
public class ChatModelSpanContentObservationHandler
implements ObservationHandler {
}
This should not be necessary. Spring AI should either extend ChatModelPromptContentObservationHandler to also tag spans, or provide a dedicated ChatModelSpanContentObservationHandler that is auto-configured when tracing is on the classpath.
Suggested Fix
Extend ChatModelPromptContentObservationHandler#onStop to also call span.tag(...) when a TracingContext is present, similar to the workaround above.
Provide equivalent coverage for tool call observation contexts.
Auto-configure these handlers when micrometer-tracing is on the classpath, guarded by the existing spring.ai.chat.observations.include-prompt / include-completion flags.