Skip to content

Commit af11fc7

Browse files
feat(genai-anthropic): wrappers for async messages methods (#4346)
* wip: wrappers for async message wrappers. * wip: replacing the invocation response attributes with new attributes function. * wip: cleaning up the function. * polish: cleaning up the response attribution using event accumulation method. * wip: rearranging the code in anthropic wrappers. * polish: added a changelog. * wip: fixing lint, typecheck and precommit failures. * wip: keeping anthropic wrapper same as openai wrapper. * wip: removing redundant truthy checks for required attributes.
1 parent 7384e45 commit af11fc7

File tree

6 files changed

+911
-185
lines changed

6 files changed

+911
-185
lines changed

instrumentation-genai/opentelemetry-instrumentation-anthropic/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Add async Anthropic message stream wrappers and manager wrappers, with wrapper
13+
tests ([#4346](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4346))
14+
- `AsyncMessagesStreamWrapper` for async message stream telemetry
15+
- `AsyncMessagesStreamManagerWrapper` for async `Messages.stream()` telemetry
1216
- Add sync streaming support for `Messages.create(stream=True)` and `Messages.stream()`
1317
([#4155](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4155))
1418
- `StreamWrapper` for handling `Messages.create(stream=True)` telemetry
@@ -22,4 +26,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2226
- Captures response attributes: `gen_ai.response.id`, `gen_ai.response.model`, `gen_ai.response.finish_reasons`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`
2327
- Error handling with `error.type` attribute
2428
- Minimum supported anthropic version is 0.16.0 (SDK uses modern `anthropic.resources.messages` module structure introduced in this version)
25-

instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/messages_extractors.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
)
3030
from opentelemetry.util.genai.types import (
3131
InputMessage,
32+
LLMInvocation,
3233
MessagePart,
3334
OutputMessage,
3435
)
@@ -153,6 +154,37 @@ def get_output_messages_from_message(
153154
]
154155

155156

157+
def set_invocation_response_attributes(
158+
invocation: LLMInvocation,
159+
message: Message | None,
160+
capture_content: bool,
161+
) -> None:
162+
if message is None:
163+
return
164+
165+
invocation.response_model_name = message.model
166+
invocation.response_id = message.id
167+
168+
finish_reason = normalize_finish_reason(message.stop_reason)
169+
if finish_reason:
170+
invocation.finish_reasons = [finish_reason]
171+
172+
tokens = extract_usage_tokens(message.usage)
173+
invocation.input_tokens = tokens.input_tokens
174+
invocation.output_tokens = tokens.output_tokens
175+
if tokens.cache_creation_input_tokens is not None:
176+
invocation.attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] = (
177+
tokens.cache_creation_input_tokens
178+
)
179+
if tokens.cache_read_input_tokens is not None:
180+
invocation.attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = (
181+
tokens.cache_read_input_tokens
182+
)
183+
184+
if capture_content:
185+
invocation.output_messages = get_output_messages_from_message(message)
186+
187+
156188
def extract_params( # pylint: disable=too-many-locals
157189
*,
158190
max_tokens: int | None = None,

instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/patch.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
"""Patching functions for Anthropic instrumentation."""
1616

17+
from __future__ import annotations
18+
1719
import logging
1820
from typing import TYPE_CHECKING, Any, Callable, Union, cast
1921

@@ -56,7 +58,7 @@ def messages_create(
5658
Union[
5759
"AnthropicMessage",
5860
"AnthropicStream[RawMessageStreamEvent]",
59-
MessagesStreamWrapper,
61+
MessagesStreamWrapper[None],
6062
],
6163
]:
6264
"""Wrap the `create` method of the `Messages` class to trace it."""
@@ -76,7 +78,7 @@ def traced_method(
7678
) -> Union[
7779
"AnthropicMessage",
7880
"AnthropicStream[RawMessageStreamEvent]",
79-
MessagesStreamWrapper,
81+
MessagesStreamWrapper[None],
8082
]:
8183
params = extract_params(*args, **kwargs)
8284
attributes = get_llm_request_attributes(params, instance)
@@ -121,13 +123,6 @@ def traced_method(
121123
raise
122124

123125
return cast(
124-
Callable[
125-
...,
126-
Union[
127-
"AnthropicMessage",
128-
"AnthropicStream[RawMessageStreamEvent]",
129-
MessagesStreamWrapper,
130-
],
131-
],
126+
'Callable[..., Union["AnthropicMessage", "AnthropicStream[RawMessageStreamEvent]", MessagesStreamWrapper[None]]]',
132127
traced_method,
133128
)

0 commit comments

Comments
 (0)