Skip to content

fix(anthropic): record finish_reasons for empty-content streams#4372

Open
Solaris-star wants to merge 1 commit into
traceloop:mainfrom
Solaris-star:fix/4362-anthropic-stream-empty-content
Open

fix(anthropic): record finish_reasons for empty-content streams#4372
Solaris-star wants to merge 1 commit into
traceloop:mainfrom
Solaris-star:fix/4362-anthropic-stream-empty-content

Conversation

@Solaris-star

@Solaris-star Solaris-star commented Jul 20, 2026

Copy link
Copy Markdown

Summary

Streamed Anthropic responses that end with a valid stop_reason but an empty content array recorded neither gen_ai.response.finish_reasons nor gen_ai.output.messages.

Root cause

_process_response_item only copied message_delta.stop_reason onto already-accumulated content-block events. With no content blocks, events stayed empty, so set_streaming_response_attributes early-returned.

Change

  • Store message-level stop_reason on complete_response during message_delta
  • Pass the full complete_response into set_streaming_response_attributes (still accepts the legacy bare events list used by existing tests)
  • Fallback: if events yield no finish reasons, use the message-level stop_reason
  • When content is empty but stop_reason is present, still emit an empty assistant output.messages entry

Test plan

  • test_streaming_empty_content_records_finish_reason
  • test_streaming_process_response_item_stores_message_stop_reason
  • Existing streaming tests remain compatible (bare events list still accepted)

Fixes #4362

Summary by CodeRabbit

  • Bug Fixes
    • Improved streaming response tracking when responses contain no content blocks.
    • Finish reasons are now recorded consistently from message-level stop reasons.
    • Empty assistant responses now include the correct finish reason and an empty content structure.
  • Tests
    • Added coverage for empty-content streaming responses and message-level stop reasons.

message_delta only wrote stop_reason onto per-content-block events.
When the stream had no content blocks, events stayed empty and
set_streaming_response_attributes returned early, dropping
gen_ai.response.finish_reasons. Store message-level stop_reason and use
it as a fallback.

Fixes traceloop#4362
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0e85b06-5705-47d6-b527-014a5e7e69aa

📥 Commits

Reviewing files that changed from the base of the PR and between 93429cf and 44ca690.

📒 Files selected for processing (3)
  • packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/span_utils.py
  • packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/streaming.py
  • packages/opentelemetry-instrumentation-anthropic/tests/test_semconv_span_attrs.py

📝 Walkthrough

Walkthrough

Anthropic streaming accumulation now preserves message-level stop reasons. Span attribute generation accepts complete responses, records fallback finish reasons, and emits empty assistant messages for empty-content streams. Tests cover both behaviors.

Changes

Anthropic streaming completion attributes

Layer / File(s) Summary
Message stop-reason propagation
packages/opentelemetry-instrumentation-anthropic/.../streaming.py
message_delta stop reasons are stored on the complete response and copied to existing event finish reasons before span processing.
Streaming attribute emission and validation
packages/opentelemetry-instrumentation-anthropic/.../span_utils.py, packages/opentelemetry-instrumentation-anthropic/tests/test_semconv_span_attrs.py
set_streaming_response_attributes accepts complete responses or event lists, uses message-level stop reasons as a fallback, emits empty assistant messages, and tests empty-content completion behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the main fix for empty-content Anthropic streams.
Linked Issues check ✅ Passed The PR implements #4362 by preserving message-level stop_reason and recording finish_reasons and output messages for empty-content streams.
Out of Scope Changes check ✅ Passed The changes and tests stay focused on the Anthropic streaming bug and do not introduce unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Bug Report: [anthropic] Streaming spans drop gen_ai.response.finish_reasons and gen_ai.output.messages for turns with empty content

2 participants