Summary
The Spring AI Anthropic SSE reassembly in BraintrustSpringAI.reassembleAnthropicSSE() only handles text delta events. When streaming Anthropic responses include tool use (input_json_delta) or extended thinking (thinking_delta) content blocks, these are silently dropped from the reconstructed span output. All content blocks are hardcoded as {"type": "text", "text": "..."}, losing the actual block type and non-text content.
This is distinct from #71 (which covers missing provider/embedding coverage). The Anthropic backend IS supported for Spring AI, but its streaming output reconstruction is lossy for non-text content blocks.
For comparison, the direct Anthropic SDK instrumentation in this repo (TracingHttpClient in anthropic_2_2_0) uses the official MessageAccumulator which correctly handles all content block types.
What is missing
In BraintrustSpringAI.reassembleAnthropicSSE() (lines 646–734):
1. content_block_delta only handles text (line 696)
case "content_block_delta" -> {
int index = event.has("index") ? event.get("index").asInt() : 0;
textByIndex.putIfAbsent(index, new StringBuilder());
if (event.has("delta") && event.get("delta").has("text")) {
textByIndex.get(index).append(event.get("delta").get("text").asText());
}
}
Missing delta types:
thinking_delta — has delta.thinking (not delta.text). Extended thinking content is dropped.
input_json_delta — has delta.partial_json (not delta.text). Tool use input JSON is dropped.
citations_delta — has delta.citation. Citation data is dropped.
2. Content block reconstruction hardcodes type: "text" (lines 717–725)
textByIndex.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> {
var block = mapper.createObjectNode();
block.put("type", "text"); // always "text"
block.put("text", entry.getValue().toString());
contentBlocks.add(block);
});
The original content block type from content_block_start (e.g., tool_use, thinking) is not preserved. Tool use blocks should include id, name, and input; thinking blocks should use type: "thinking".
Impact
- Tool use in streaming: When the model returns a
tool_use content block, the content_block_start event (containing id, name) is ignored beyond creating an empty buffer. The input_json_delta events are dropped. The final output has an empty {"type": "text", "text": ""} where a tool call should be.
- Extended thinking in streaming: Thinking content (
thinking_delta events) is dropped. The span output loses all chain-of-thought reasoning.
- Non-streaming calls are unaffected — the full response JSON is captured correctly.
Braintrust docs status
Upstream sources
Local files inspected
braintrust-sdk/instrumentation/springai_1_0_0/src/main/java/dev/braintrust/instrumentation/springai/v1_0_0/BraintrustSpringAI.java — lines 646–734 (reassembleAnthropicSSE: only delta.text handled at line 696; content blocks hardcoded as type: "text" at line 722)
braintrust-sdk/instrumentation/anthropic_2_2_0/src/main/java/dev/braintrust/instrumentation/anthropic/v2_2_0/TracingHttpClient.java — lines 340–368 (direct SDK path uses MessageAccumulator which handles all event types correctly)
braintrust-sdk/instrumentation/springai_1_0_0/src/test/java/dev/braintrust/instrumentation/springai/v1_0_0/BraintrustSpringAITest.java — no streaming tests exercise tool use or extended thinking responses
Summary
The Spring AI Anthropic SSE reassembly in
BraintrustSpringAI.reassembleAnthropicSSE()only handlestextdelta events. When streaming Anthropic responses include tool use (input_json_delta) or extended thinking (thinking_delta) content blocks, these are silently dropped from the reconstructed span output. All content blocks are hardcoded as{"type": "text", "text": "..."}, losing the actual block type and non-text content.This is distinct from #71 (which covers missing provider/embedding coverage). The Anthropic backend IS supported for Spring AI, but its streaming output reconstruction is lossy for non-text content blocks.
For comparison, the direct Anthropic SDK instrumentation in this repo (
TracingHttpClientinanthropic_2_2_0) uses the officialMessageAccumulatorwhich correctly handles all content block types.What is missing
In
BraintrustSpringAI.reassembleAnthropicSSE()(lines 646–734):1.
content_block_deltaonly handlestext(line 696)Missing delta types:
thinking_delta— hasdelta.thinking(notdelta.text). Extended thinking content is dropped.input_json_delta— hasdelta.partial_json(notdelta.text). Tool use input JSON is dropped.citations_delta— hasdelta.citation. Citation data is dropped.2. Content block reconstruction hardcodes
type: "text"(lines 717–725)The original content block type from
content_block_start(e.g.,tool_use,thinking) is not preserved. Tool use blocks should includeid,name, andinput; thinking blocks should usetype: "thinking".Impact
tool_usecontent block, thecontent_block_startevent (containingid,name) is ignored beyond creating an empty buffer. Theinput_json_deltaevents are dropped. The final output has an empty{"type": "text", "text": ""}where a tool call should be.thinking_deltaevents) is dropped. The span output loses all chain-of-thought reasoning.Braintrust docs status
Upstream sources
thinking_delta,input_json_delta,citations_deltaevent types alongsidetext_deltathinkingcontent blocks in streamingtool_usecontent blocks withinput_json_deltain streamingLocal files inspected
braintrust-sdk/instrumentation/springai_1_0_0/src/main/java/dev/braintrust/instrumentation/springai/v1_0_0/BraintrustSpringAI.java— lines 646–734 (reassembleAnthropicSSE: onlydelta.texthandled at line 696; content blocks hardcoded astype: "text"at line 722)braintrust-sdk/instrumentation/anthropic_2_2_0/src/main/java/dev/braintrust/instrumentation/anthropic/v2_2_0/TracingHttpClient.java— lines 340–368 (direct SDK path usesMessageAccumulatorwhich handles all event types correctly)braintrust-sdk/instrumentation/springai_1_0_0/src/test/java/dev/braintrust/instrumentation/springai/v1_0_0/BraintrustSpringAITest.java— no streaming tests exercise tool use or extended thinking responses