Skip to content

Commit ce69875

Browse files
polish: copilot comments.
1 parent b3b1c35 commit ce69875

5 files changed

Lines changed: 407 additions & 2 deletions

File tree

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,11 @@ def __init__(
381381
async def __aenter__(
382382
self,
383383
) -> AsyncMessagesStreamWrapper[ResponseFormatT]:
384-
msg_stream = await self._manager.__aenter__()
384+
try:
385+
msg_stream = await self._manager.__aenter__()
386+
except Exception as exc:
387+
self._invocation.fail(exc)
388+
raise
385389
self._stream_wrapper = AsyncMessagesStreamWrapper(
386390
msg_stream,
387391
self._invocation,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
interactions:
2+
- request:
3+
body: |-
4+
{
5+
"max_tokens": 100,
6+
"messages": [
7+
{
8+
"role": "user",
9+
"content": "Say hello in one word."
10+
}
11+
],
12+
"model": "claude-sonnet-4-20250514",
13+
"stream": true
14+
}
15+
headers:
16+
Accept:
17+
- application/json
18+
Accept-Encoding:
19+
- gzip, deflate
20+
Connection:
21+
- keep-alive
22+
Content-Length:
23+
- '131'
24+
Content-Type:
25+
- application/json
26+
Host:
27+
- api.anthropic.com
28+
User-Agent:
29+
- Anthropic/Python 0.96.0
30+
X-Stainless-Arch:
31+
- arm64
32+
X-Stainless-Async:
33+
- 'false'
34+
X-Stainless-Helper-Method:
35+
- stream
36+
X-Stainless-Lang:
37+
- python
38+
X-Stainless-OS:
39+
- MacOS
40+
X-Stainless-Package-Version:
41+
- 0.96.0
42+
X-Stainless-Runtime:
43+
- CPython
44+
X-Stainless-Runtime-Version:
45+
- 3.13.9
46+
X-Stainless-Stream-Helper:
47+
- messages
48+
anthropic-version:
49+
- '2023-06-01'
50+
x-api-key:
51+
- test_anthropic_api_key
52+
x-stainless-read-timeout:
53+
- '600'
54+
x-stainless-retry-count:
55+
- '0'
56+
x-stainless-timeout:
57+
- NOT_GIVEN
58+
method: POST
59+
uri: https://api.anthropic.com/v1/messages
60+
response:
61+
body:
62+
string: |+
63+
event: message_start
64+
data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01Bso8Ps9vpszQBB5vFUaWQx","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} }
65+
66+
event: content_block_start
67+
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} }
68+
69+
event: ping
70+
data: {"type": "ping"}
71+
72+
event: content_block_delta
73+
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"}}
74+
75+
event: content_block_stop
76+
data: {"type":"content_block_stop","index":0 }
77+
78+
event: message_delta
79+
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null,"stop_details":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} }
80+
81+
event: message_stop
82+
data: {"type":"message_stop"}
83+
84+
headers:
85+
CF-RAY:
86+
- 9f62cebe3f501906-EWR
87+
Cache-Control:
88+
- no-cache
89+
Connection:
90+
- keep-alive
91+
Content-Security-Policy:
92+
- default-src 'none'; frame-ancestors 'none'
93+
Content-Type:
94+
- text/event-stream; charset=utf-8
95+
Date:
96+
- Sun, 03 May 2026 22:47:28 GMT
97+
Server:
98+
- cloudflare
99+
Transfer-Encoding:
100+
- chunked
101+
X-Robots-Tag:
102+
- none
103+
anthropic-ratelimit-input-tokens-limit:
104+
- '450000'
105+
anthropic-ratelimit-input-tokens-remaining:
106+
- '450000'
107+
anthropic-ratelimit-input-tokens-reset:
108+
- '2026-05-03T22:47:27Z'
109+
anthropic-ratelimit-output-tokens-limit:
110+
- '90000'
111+
anthropic-ratelimit-output-tokens-remaining:
112+
- '90000'
113+
anthropic-ratelimit-output-tokens-reset:
114+
- '2026-05-03T22:47:27Z'
115+
anthropic-ratelimit-requests-limit:
116+
- '1000'
117+
anthropic-ratelimit-requests-remaining:
118+
- '999'
119+
anthropic-ratelimit-requests-reset:
120+
- '2026-05-03T22:47:27Z'
121+
anthropic-ratelimit-tokens-limit:
122+
- '540000'
123+
anthropic-ratelimit-tokens-remaining:
124+
- '540000'
125+
anthropic-ratelimit-tokens-reset:
126+
- '2026-05-03T22:47:27Z'
127+
cf-cache-status:
128+
- DYNAMIC
129+
content-length:
130+
- '1154'
131+
request-id:
132+
- req_011CagT1x9aQyaFmxJ5GNARp
133+
set-cookie:
134+
- _cfuvid=FJE3MQyoJt8lkF4.Kt2pCOiULwrAWJefeAJPW3dEadE-1777848447.712399-1.0.1.1-jh2AssDriF8MBsq43fuPcFLny3DvtSkWWHqOo0x8k18;
135+
HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com
136+
strict-transport-security:
137+
- max-age=31536000; includeSubDomains; preload
138+
traceresponse:
139+
- 00-ca1e689f10c6d029831c2bac29080b26-ec7932028676ce57-01
140+
vary:
141+
- Accept-Encoding
142+
x-envoy-upstream-service-time:
143+
- '881'
144+
status:
145+
code: 200
146+
message: OK
147+
version: 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
interactions:
2+
- request:
3+
body: |-
4+
{
5+
"max_tokens": 100,
6+
"messages": [
7+
{
8+
"role": "user",
9+
"content": "Say hello in one word."
10+
}
11+
],
12+
"model": "claude-sonnet-4-20250514",
13+
"stream": true
14+
}
15+
headers:
16+
Accept:
17+
- application/json
18+
Accept-Encoding:
19+
- gzip, deflate
20+
Connection:
21+
- keep-alive
22+
Content-Length:
23+
- '131'
24+
Content-Type:
25+
- application/json
26+
Host:
27+
- api.anthropic.com
28+
User-Agent:
29+
- Anthropic/Python 0.96.0
30+
X-Stainless-Arch:
31+
- arm64
32+
X-Stainless-Async:
33+
- 'false'
34+
X-Stainless-Helper-Method:
35+
- stream
36+
X-Stainless-Lang:
37+
- python
38+
X-Stainless-OS:
39+
- MacOS
40+
X-Stainless-Package-Version:
41+
- 0.96.0
42+
X-Stainless-Runtime:
43+
- CPython
44+
X-Stainless-Runtime-Version:
45+
- 3.13.9
46+
X-Stainless-Stream-Helper:
47+
- messages
48+
anthropic-version:
49+
- '2023-06-01'
50+
x-api-key:
51+
- test_anthropic_api_key
52+
x-stainless-read-timeout:
53+
- '600'
54+
x-stainless-retry-count:
55+
- '0'
56+
x-stainless-timeout:
57+
- NOT_GIVEN
58+
method: POST
59+
uri: https://api.anthropic.com/v1/messages
60+
response:
61+
body:
62+
string: |+
63+
event: message_start
64+
data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01ArZ4yKLwhfUF16CVjeZSWf","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}}}
65+
66+
event: content_block_start
67+
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} }
68+
69+
event: ping
70+
data: {"type": "ping"}
71+
72+
event: content_block_delta
73+
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} }
74+
75+
event: content_block_stop
76+
data: {"type":"content_block_stop","index":0 }
77+
78+
event: message_delta
79+
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null,"stop_details":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5}}
80+
81+
event: message_stop
82+
data: {"type":"message_stop" }
83+
84+
headers:
85+
CF-RAY:
86+
- 9f62ceb70f4658c1-EWR
87+
Cache-Control:
88+
- no-cache
89+
Connection:
90+
- keep-alive
91+
Content-Security-Policy:
92+
- default-src 'none'; frame-ancestors 'none'
93+
Content-Type:
94+
- text/event-stream; charset=utf-8
95+
Date:
96+
- Sun, 03 May 2026 22:47:27 GMT
97+
Server:
98+
- cloudflare
99+
Transfer-Encoding:
100+
- chunked
101+
X-Robots-Tag:
102+
- none
103+
anthropic-ratelimit-input-tokens-limit:
104+
- '450000'
105+
anthropic-ratelimit-input-tokens-remaining:
106+
- '450000'
107+
anthropic-ratelimit-input-tokens-reset:
108+
- '2026-05-03T22:47:26Z'
109+
anthropic-ratelimit-output-tokens-limit:
110+
- '90000'
111+
anthropic-ratelimit-output-tokens-remaining:
112+
- '90000'
113+
anthropic-ratelimit-output-tokens-reset:
114+
- '2026-05-03T22:47:26Z'
115+
anthropic-ratelimit-requests-limit:
116+
- '1000'
117+
anthropic-ratelimit-requests-remaining:
118+
- '999'
119+
anthropic-ratelimit-requests-reset:
120+
- '2026-05-03T22:47:26Z'
121+
anthropic-ratelimit-tokens-limit:
122+
- '540000'
123+
anthropic-ratelimit-tokens-remaining:
124+
- '540000'
125+
anthropic-ratelimit-tokens-reset:
126+
- '2026-05-03T22:47:26Z'
127+
cf-cache-status:
128+
- DYNAMIC
129+
content-length:
130+
- '1166'
131+
request-id:
132+
- req_011CagT1sFeNm1tViDNAWHeZ
133+
set-cookie:
134+
- _cfuvid=rj75w_GU5xV9cWP7nOfJwscHGqT_ppwRllzb6fSv0Fw-1777848446.5684419-1.0.1.1-f0Kmq9td0Tj1eY06Rwpq86V7Zainu268u8hWFi4QmtY;
135+
HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com
136+
strict-transport-security:
137+
- max-age=31536000; includeSubDomains; preload
138+
traceresponse:
139+
- 00-f6a597a36c8af29bd5bd3faa514a8df9-8263b57a1c59d381-01
140+
vary:
141+
- Accept-Encoding
142+
x-envoy-upstream-service-time:
143+
- '871'
144+
status:
145+
code: 200
146+
message: OK
147+
version: 1

instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_async_wrappers.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,18 @@ def __exit__(self, exc_type, exc_val, exc_tb):
115115

116116

117117
class _FakeAsyncManager:
118-
def __init__(self, stream, suppressed=False, exit_error=None):
118+
def __init__(
119+
self, stream, suppressed=False, enter_error=None, exit_error=None
120+
):
119121
self._stream = stream
120122
self._suppressed = suppressed
123+
self._enter_error = enter_error
121124
self._exit_error = exit_error
122125
self.exit_args = None
123126

124127
async def __aenter__(self):
128+
if self._enter_error is not None:
129+
raise self._enter_error
125130
return self._stream
126131

127132
async def __aexit__(self, exc_type, exc_val, exc_tb):
@@ -474,6 +479,28 @@ async def test_async_manager_enter_constructs_async_stream_wrapper():
474479
assert wrapper._stream_wrapper is result
475480

476481

482+
@pytest.mark.asyncio
483+
async def test_async_manager_enter_fails_invocation_when_manager_raises():
484+
error = RuntimeError("manager enter failure")
485+
failures = []
486+
invocation = _make_invocation()
487+
invocation.fail = failures.append
488+
wrapper = AsyncMessagesStreamManagerWrapper(
489+
manager=_FakeAsyncManager(
490+
stream=SimpleNamespace(),
491+
enter_error=error,
492+
),
493+
invocation=invocation,
494+
capture_content=False,
495+
)
496+
497+
with pytest.raises(RuntimeError, match="manager enter failure"):
498+
async with wrapper:
499+
pass
500+
501+
assert failures == [error]
502+
503+
477504
@pytest.mark.asyncio
478505
async def test_async_manager_exit_forwards_exception_to_stream_wrapper():
479506
wrapper = AsyncMessagesStreamManagerWrapper(

0 commit comments

Comments
 (0)