Skip to content

[BOT ISSUE] BraintrustStream cannot aggregate Anthropic Messages streaming events #38

@braintrust-bot

Description

@braintrust-bot

Summary

The BraintrustStream streaming aggregator only handles OpenAI Chat Completions chunk format (choices[].delta). Anthropic's Messages streaming API uses a structurally different SSE event protocol (message_start, content_block_delta, message_delta, etc.) that the aggregator cannot parse. All Anthropic streaming chunks are silently discarded, producing an empty aggregated result.

What is missing

Anthropic's Messages streaming API emits typed SSE events:

  • message_start — contains the initial Message object with usage.input_tokens
  • content_block_start — opens a content block (text, tool_use, or thinking)
  • content_block_delta — incremental content: text_delta.text, input_json_delta.partial_json, or thinking_delta.thinking
  • content_block_stop — closes a content block
  • message_delta — final stop_reason and cumulative usage.output_tokens
  • message_stop — stream termination

These events have no choices array and no delta sub-field inside choices. The current aggregation path in BraintrustStream::aggregate() (src/stream.rs:727-807) parses each chunk as a StreamChunk expecting choices: Vec<StreamChoice>. Anthropic events fail deserialization and hit the Err(_) => continue fallback at line 740-741, silently dropping all data.

This means:

  • Text content from content_block_delta events is lost
  • Tool use arguments streamed via input_json_delta are lost
  • Extended thinking content from thinking_delta events is lost
  • Usage metrics from message_start and message_delta are lost
  • Stop reason from message_delta is lost

Braintrust's other SDKs handle Anthropic streaming via dedicated wrappers:

  • TypeScript: wrapAnthropic
  • Python: wrap_anthropic
  • Go: tracing middleware
  • Ruby: Braintrust::Trace::Anthropic.wrap
  • Java/C#: interceptors/extensions

All of these capture streamed Anthropic responses including content, tool use, and usage metrics.

This is distinct from existing issues:

Braintrust docs status

Braintrust documents Anthropic streaming support across all non-Rust SDKs: "Braintrust handles streaming, metric collection (including cached tokens), and other details." Status: supported in Braintrust platform via other SDK wrappers, not instrumented in this Rust SDK.

Upstream sources

Local files inspected

  • src/stream.rsStreamChunk struct (line 639-647) only has model, choices, usage fields; StreamChoice/StreamDelta (lines 651-664) expect OpenAI format; aggregate() (lines 727-807) parses only choices[].delta; error fallback at line 740-741 silently skips unparseable chunks
  • src/extractors.rsextract_anthropic_usage() handles the non-streaming Anthropic response format correctly, but is not invoked during streaming aggregation
  • src/lib.rswrap_stream_with_span is exported as the primary streaming instrumentation surface

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions