fix(anthropic): Only finish relevant spans in .create() patches by alexander-alderman-webb · Pull Request #5716 · getsentry/sentry-python
Description
Anthropic spans are only created in _sentry_patched_create() and _sentry_patched_create_async().
Exit spans in these functions when the anthropic library function raises an exception.
Resolves an edge case where _sentry_patched_create() or _sentry_patched_create_async() exits early and does not create a span and get_current_span() therefore returns a non-anthropic span.
Issues
Reminders
- Please add tests to validate your changes, and lint your code using
tox -e linters. - Add GH Issue ID & Linear ID (if applicable)
- PR title should use conventional commit style (
feat:,fix:,ref:,meta:) - For external contributors: CONTRIBUTING.md, Sentry SDK development docs, Discord community
Semver Impact of This PR
🟢 Patch (bug fixes)
📋 Changelog Preview
This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).
New Features ✨
- (ai) Redact base64 data URLs in image_url content blocks by ericapisani in
#5953 - (integrations) Instrument pyreqwest tracing by servusdei2018 in
#5682
Bug Fixes 🐛
- (anthropic) Only finish relevant spans in .create() patches by alexander-alderman-webb in
#5716
- (wsgi) Respect HTTP_X_FORWARDED_PROTO in request.url construction by sl0thentr0py in
#5963
Internal Changes 🔧
- (anthropic) Separate sync and async .create() patches by alexander-alderman-webb in
#5715 - (openai) Split token counting by API for easier deprecation by ericapisani in
#5930 - (opentelemetry) Ignore mypy error by alexander-alderman-webb in
#5927 - Fix license metadata in setup.py by sl0thentr0py in
#5934 - Update validate-pr workflow by stephanie-anderson in
#5931
🤖 This preview updates automatically when you update the PR.
Codecov Results 📊
✅ 13 passed | Total: 13 | Pass Rate: 100% | Execution Time: 8.36s
All tests are passing successfully.
❌ Patch coverage is 20.00%. Project has 14846 uncovered lines.
Files with missing lines (1)
| File | Patch % | Lines |
|---|---|---|
anthropic.py |
5.81% |
Generated by Codecov Action
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any test coverage that we can add here to confirm this behaviour? Otherwise LGTM
Is there any test coverage that we can add here to confirm this behaviour? Otherwise LGTM
In my view a regression test has limited use in this case because an integration should not be touching spans created outside the integration (unless it's for an agreed-upon reason, i.e., not duplicating spans between openai-agents and openai).
For the future, here's a script that verifies that demonstrates the edge case I'm addressing below. The messages argument is missing from the call to create() and the outer transaction is closed prematurely since it's status is INTERNAL_ERROR.
import sentry_sdk from sentry_sdk.tracing import SPANSTATUS from sentry_sdk.integrations.anthropic import AnthropicIntegration from anthropic import Anthropic sentry_sdk.init( dsn="", integrations=[AnthropicIntegration()], traces_sample_rate=1.0, ) def main(): client = Anthropic(api_key="dummy") with sentry_sdk.start_transaction(name="parent", op="test") as transaction: transaction.set_status(SPANSTATUS.INTERNAL_ERROR) try: client.messages.create(max_tokens=1024, model="model", stream=False) except Exception as e: print(f"Expected exception: {e}") if __name__ == "__main__": main()
In my view a regression test has limited use in this case because an integration should not be touching spans created outside the integration (unless it's for an agreed-upon reason, i.e., not duplicating spans between openai-agents and openai).
I agree with what you're saying regarding an integration not touching spans created outside of it, but I think we could ensure that this edge case is resolved with a test that confirms that an anthropic span is present when the conditions that caused it are set.
To give a picture of what I'm envisioning (I used the script you've provided as the basis for it):
sentry_init(integrations=[AnthropicIntegration()], traces_sample_rate=1.0) events = capture_events() client = Anthropic(api_key="z") client.messages._post = mock.Mock(return_value=EXAMPLE_MESSAGE) with start_transaction(name="anthropic") as transaction: transaction.set_status(SPANSTATUS.INTERNAL_ERROR) client.messages.create( # No messages value passed in model="model", max_tokens=1024, stream=False ) (tx,) = events assert len(tx["spans"]) == 1 span = tx["spans"][0] assert span["data"][SPANDATA.GEN_AI_SYSTEM] == "anthropic"
What do you think? Also happy to pair on this tomorrow when you're back online 😄
Base automatically changed from webb/anthropic/separate-sync-and-async to master
