Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
697d621
feat(anthropic): Emit gen_ai.chat spans for asynchronous messages.str…
alexander-alderman-webb Mar 2, 2026
321d754
add tests
alexander-alderman-webb Mar 2, 2026
4fc7ebe
.
alexander-alderman-webb Mar 2, 2026
f4859a5
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 2, 2026
2c86293
.
alexander-alderman-webb Mar 2, 2026
81e1d75
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 11, 2026
244ea77
.
alexander-alderman-webb Mar 11, 2026
57d8633
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 11, 2026
f9bc730
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 11, 2026
21b97ba
tests
alexander-alderman-webb Mar 11, 2026
7660240
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 12, 2026
c0632a3
fix types
alexander-alderman-webb Mar 12, 2026
8f54d58
.
alexander-alderman-webb Mar 12, 2026
c0f3c0b
typing
alexander-alderman-webb Mar 12, 2026
553d968
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 12, 2026
86ab957
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 12, 2026
ee09623
Merge branch 'master' into webb/anthropic/new-async-patch
alexander-alderman-webb Mar 12, 2026
12ba285
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 12, 2026
39f1cf0
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 12, 2026
34f863b
simplify setting input data
alexander-alderman-webb Mar 12, 2026
f187ad5
fix self annotation
alexander-alderman-webb Mar 12, 2026
851eec7
Merge branch 'webb/anthropic/new-patch' into webb/anthropic/new-async…
alexander-alderman-webb Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 102 additions & 3 deletions sentry_sdk/integrations/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@

from anthropic import Stream, AsyncStream
from anthropic.resources import AsyncMessages, Messages
from anthropic.lib.streaming._messages import MessageStreamManager
from anthropic.lib.streaming._messages import (
MessageStreamManager,
AsyncMessageStreamManager,
)

from anthropic.types import (
MessageStartEvent,
Expand Down Expand Up @@ -68,6 +71,7 @@
TextBlockParam,
ToolUnionParam,
MessageStream,
AsyncMessageStream,
)


Expand Down Expand Up @@ -98,6 +102,13 @@ def setup_once() -> None:
MessageStreamManager.__enter__
)

AsyncMessages.stream = _wrap_async_message_stream(AsyncMessages.stream)
AsyncMessageStreamManager.__aenter__ = (
_wrap_async_message_stream_manager_aenter(
AsyncMessageStreamManager.__aenter__
)
)


def _capture_exception(exc: "Any") -> None:
set_span_errored()
Expand Down Expand Up @@ -448,10 +459,10 @@ def _wrap_synchronous_message_iterator(


async def _wrap_asynchronous_message_iterator(
iterator: "AsyncIterator[RawMessageStreamEvent]",
iterator: "AsyncIterator[Union[RawMessageStreamEvent, MessageStreamEvent]]",
span: "Span",
integration: "AnthropicIntegration",
) -> "AsyncIterator[RawMessageStreamEvent]":
) -> "AsyncIterator[Union[RawMessageStreamEvent, MessageStreamEvent]]":
"""
Sets information received while iterating the response stream on the AI Client Span.
Responsible for closing the AI Client Span.
Expand Down Expand Up @@ -793,6 +804,94 @@ def _sentry_patched_enter(self: "MessageStreamManager") -> "MessageStream":
return _sentry_patched_enter


def _wrap_async_message_stream(f: "Any") -> "Any":
"""
Attaches user-provided arguments to the returned context manager.
The attributes are set on AI Client Spans in the patch for the context manager.
"""

@wraps(f)
def _sentry_patched_stream(
*args: "Any", **kwargs: "Any"
) -> "AsyncMessageStreamManager":
stream_manager = f(*args, **kwargs)

stream_manager._max_tokens = kwargs.get("max_tokens")
stream_manager._messages = kwargs.get("messages")
stream_manager._model = kwargs.get("model")
stream_manager._system = kwargs.get("system")
stream_manager._temperature = kwargs.get("temperature")
stream_manager._top_k = kwargs.get("top_k")
stream_manager._top_p = kwargs.get("top_p")
stream_manager._tools = kwargs.get("tools")

return stream_manager

return _sentry_patched_stream


def _wrap_async_message_stream_manager_aenter(f: "Any") -> "Any":
"""
Creates and manages AI Client Spans.
"""

@wraps(f)
async def _sentry_patched_aenter(
self: "AsyncMessageStreamManager",
) -> "AsyncMessageStream":
stream = await f(self)
if not hasattr(self, "_max_tokens"):
return stream

integration = sentry_sdk.get_client().get_integration(AnthropicIntegration)

if integration is None:
return stream

if self._messages is None:
return stream

try:
iter(self._messages)
except TypeError:
return stream

model = self._model
if model is None:
model = ""

span = get_start_span_function()(
op=OP.GEN_AI_CHAT,
name=f"chat {model}".strip(),
origin=AnthropicIntegration.origin,
)
span.__enter__()

span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, True)
_set_common_input_data(
span=span,
integration=integration,
max_tokens=self._max_tokens,
messages=self._messages,
model=model,
system=self._system,
temperature=self._temperature,
top_k=self._top_k,
top_p=self._top_p,
tools=self._tools,
)

stream._iterator = _wrap_asynchronous_message_iterator(
iterator=stream._iterator,
span=span,
integration=integration,
)

return stream

return _sentry_patched_aenter


def _is_given(obj: "Any") -> bool:
"""
Check for givenness safely across different anthropic versions.
Expand Down
Loading
Loading