Skip to content

Commit d8c8b35

Browse files
committed
fix(anthropic): correct structured outputs API format and update tests
- Fix output_format structure to use direct 'schema' field instead of nested 'json_schema.schema' - Update span_utils.py to extract schema from correct location - Simplify tests for events mode (no request attribute checks) - Add VCR cassettes for structured outputs tests
1 parent cd7f14d commit d8c8b35

File tree

5 files changed

+331
-57
lines changed

5 files changed

+331
-57
lines changed

packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/span_utils.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,8 @@ async def aset_input_attributes(span, kwargs):
169169

170170
output_format = kwargs.get("output_format")
171171
if output_format and isinstance(output_format, dict):
172-
if output_format.get("type") == "json_schema" and output_format.get("schema"):
173-
set_span_attribute(
174-
span,
175-
SpanAttributes.LLM_REQUEST_STRUCTURED_OUTPUT_SCHEMA,
176-
json.dumps(output_format.get("schema")),
177-
)
178-
elif output_format.get("type") == "json" and output_format.get("json_schema"):
179-
schema = output_format.get("json_schema", {}).get("schema")
172+
if output_format.get("type") == "json_schema":
173+
schema = output_format.get("schema")
180174
if schema:
181175
set_span_attribute(
182176
span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
interactions:
2+
- request:
3+
body: '{"max_tokens":1024,"messages":[{"role":"user","content":"Tell me a joke
4+
about OpenTelemetry and rate it from 1 to 10"}],"model":"claude-sonnet-4-5-20250929","output_format":{"type":"json_schema","schema":{"type":"object","properties":{"joke":{"type":"string","description":"A
5+
joke about OpenTelemetry"},"rating":{"type":"integer","description":"Rating
6+
of the joke from 1 to 10"}},"required":["joke","rating"],"additionalProperties":false}}}'
7+
headers:
8+
accept:
9+
- application/json
10+
accept-encoding:
11+
- gzip, deflate
12+
anthropic-beta:
13+
- structured-outputs-2025-11-13
14+
anthropic-version:
15+
- '2023-06-01'
16+
connection:
17+
- keep-alive
18+
content-length:
19+
- '440'
20+
content-type:
21+
- application/json
22+
host:
23+
- api.anthropic.com
24+
user-agent:
25+
- Anthropic/Python 0.74.1
26+
x-stainless-arch:
27+
- arm64
28+
x-stainless-async:
29+
- 'false'
30+
x-stainless-lang:
31+
- python
32+
x-stainless-os:
33+
- MacOS
34+
x-stainless-package-version:
35+
- 0.74.1
36+
x-stainless-read-timeout:
37+
- '600'
38+
x-stainless-retry-count:
39+
- '0'
40+
x-stainless-runtime:
41+
- CPython
42+
x-stainless-runtime-version:
43+
- 3.11.7
44+
x-stainless-timeout:
45+
- '600'
46+
method: POST
47+
uri: https://api.anthropic.com/v1/messages?beta=true
48+
response:
49+
body:
50+
string: !!binary |
51+
H4sIAAAAAAAAAwAAAP//dJFPaxsxEMW/ynTOMtjbuK11CaTnkmASeuiWRZFevKq10lYapdma/e5l
52+
TU3/0dPA+703j2FOPCSHwJptMNVhVVKMkNXVartq1s12vWt2rNg71jyUQ7fe7PdV5P3mw3e32T/c
53+
TXdv3g32ZsuKZRqxuFCKOYAV5xQWwZTii5gorNimKIjC+tPp4he8LOQ8NJ9a/pKOaFlTyx/7iZx3
54+
JD3I4Rkhjcj0mGGOVEf65qWn2xHxHgEDJE/XdANrasESmag3jiQlGkycSLKxKJSeFuYz4YV8PK8O
55+
6VBetayo5WzEx8PS/nbm+bPiImnsMkxJkTUjuk5qjvwTFHytiBasYw1BcT1frk/s41ilk3RELKyb
56+
plFsje3R2QwjPsXuT8f6wjOM+x+7ZJcCjD0GZBO67fCv/xfd9H/TWXGq8rv0eqe4ID97i048Mmte
57+
/uVMdjzPPwAAAP//AwD+H/OiIgIAAA==
58+
headers:
59+
CF-RAY:
60+
- 9a30f7a7b8d37da0-TLV
61+
Connection:
62+
- keep-alive
63+
Content-Encoding:
64+
- gzip
65+
Content-Type:
66+
- application/json
67+
Date:
68+
- Sun, 23 Nov 2025 13:21:05 GMT
69+
Server:
70+
- cloudflare
71+
Transfer-Encoding:
72+
- chunked
73+
X-Robots-Tag:
74+
- none
75+
anthropic-organization-id:
76+
- 617d109c-a187-4902-889d-689223d134aa
77+
anthropic-ratelimit-input-tokens-limit:
78+
- '2000000'
79+
anthropic-ratelimit-input-tokens-remaining:
80+
- '2000000'
81+
anthropic-ratelimit-input-tokens-reset:
82+
- '2025-11-23T13:21:04Z'
83+
anthropic-ratelimit-output-tokens-limit:
84+
- '400000'
85+
anthropic-ratelimit-output-tokens-remaining:
86+
- '400000'
87+
anthropic-ratelimit-output-tokens-reset:
88+
- '2025-11-23T13:21:05Z'
89+
anthropic-ratelimit-tokens-limit:
90+
- '2400000'
91+
anthropic-ratelimit-tokens-remaining:
92+
- '2400000'
93+
anthropic-ratelimit-tokens-reset:
94+
- '2025-11-23T13:21:04Z'
95+
cf-cache-status:
96+
- DYNAMIC
97+
request-id:
98+
- req_011CVQtbM68bFJThHxEH4KeC
99+
retry-after:
100+
- '59'
101+
strict-transport-security:
102+
- max-age=31536000; includeSubDomains; preload
103+
x-envoy-upstream-service-time:
104+
- '3058'
105+
status:
106+
code: 200
107+
message: OK
108+
version: 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
interactions:
2+
- request:
3+
body: '{"max_tokens":1024,"messages":[{"role":"user","content":"Tell me a joke
4+
about OpenTelemetry and rate it from 1 to 10"}],"model":"claude-sonnet-4-5-20250929","output_format":{"type":"json_schema","schema":{"type":"object","properties":{"joke":{"type":"string","description":"A
5+
joke about OpenTelemetry"},"rating":{"type":"integer","description":"Rating
6+
of the joke from 1 to 10"}},"required":["joke","rating"],"additionalProperties":false}}}'
7+
headers:
8+
accept:
9+
- application/json
10+
accept-encoding:
11+
- gzip, deflate
12+
anthropic-beta:
13+
- structured-outputs-2025-11-13
14+
anthropic-version:
15+
- '2023-06-01'
16+
connection:
17+
- keep-alive
18+
content-length:
19+
- '440'
20+
content-type:
21+
- application/json
22+
host:
23+
- api.anthropic.com
24+
user-agent:
25+
- Anthropic/Python 0.74.1
26+
x-stainless-arch:
27+
- arm64
28+
x-stainless-async:
29+
- 'false'
30+
x-stainless-lang:
31+
- python
32+
x-stainless-os:
33+
- MacOS
34+
x-stainless-package-version:
35+
- 0.74.1
36+
x-stainless-read-timeout:
37+
- '600'
38+
x-stainless-retry-count:
39+
- '0'
40+
x-stainless-runtime:
41+
- CPython
42+
x-stainless-runtime-version:
43+
- 3.11.7
44+
x-stainless-timeout:
45+
- '600'
46+
method: POST
47+
uri: https://api.anthropic.com/v1/messages?beta=true
48+
response:
49+
body:
50+
string: !!binary |
51+
H4sIAAAAAAAAA3SRX4vbMBDEv8p2nxVI3AvH6aVw5aBQaF8KV2iKUa2prYu90knrNCb4uxe7Df1H
52+
nxbmN7M7SBceokfPlpvejR6bEkWgm5vNflNtq/32rrpjw8Gz5aG09XZ3++bdM874+PD68f70NrTn
53+
+CBPOzasU8LiQimuBRvOsV8EV0oo6kTZcBNFIcr20+XqV5wXsg7LlwM/xSMObOnAj91EPnjSDuRx
54+
Qh8TMn3JcEcaE30L2tH7BPmAHgM0T6/oHo0bC5bIRJ3zpDHS4GSikpwUil/J6VIhRCEnP3Zn9G4R
55+
ShfSGpJIa9OzUsoxuXbFLw5s6MDZaZB2aXg78/zZcNGY6gxXorBliK91zMI/QcHzCGnAVsa+Nzyu
56+
r2MvHCSNWms8QgrbqqoMN67pUDcZ6736T8f2yjOc/x+7ZpcDSB0GZNfX++Ff/y+66/6ms+E46u/S
57+
zUvDBfkUGtQakNny8qfeZc/z/B0AAP//AwA+knxDRgIAAA==
58+
headers:
59+
CF-RAY:
60+
- 9a30f7bc0cccf169-TLV
61+
Connection:
62+
- keep-alive
63+
Content-Encoding:
64+
- gzip
65+
Content-Type:
66+
- application/json
67+
Date:
68+
- Sun, 23 Nov 2025 13:21:09 GMT
69+
Server:
70+
- cloudflare
71+
Transfer-Encoding:
72+
- chunked
73+
X-Robots-Tag:
74+
- none
75+
anthropic-organization-id:
76+
- 617d109c-a187-4902-889d-689223d134aa
77+
anthropic-ratelimit-input-tokens-limit:
78+
- '2000000'
79+
anthropic-ratelimit-input-tokens-remaining:
80+
- '2000000'
81+
anthropic-ratelimit-input-tokens-reset:
82+
- '2025-11-23T13:21:07Z'
83+
anthropic-ratelimit-output-tokens-limit:
84+
- '400000'
85+
anthropic-ratelimit-output-tokens-remaining:
86+
- '400000'
87+
anthropic-ratelimit-output-tokens-reset:
88+
- '2025-11-23T13:21:09Z'
89+
anthropic-ratelimit-tokens-limit:
90+
- '2400000'
91+
anthropic-ratelimit-tokens-remaining:
92+
- '2400000'
93+
anthropic-ratelimit-tokens-reset:
94+
- '2025-11-23T13:21:07Z'
95+
cf-cache-status:
96+
- DYNAMIC
97+
request-id:
98+
- req_011CVQtbb1HQigBLDM6oAQT3
99+
retry-after:
100+
- '53'
101+
strict-transport-security:
102+
- max-age=31536000; includeSubDomains; preload
103+
x-envoy-upstream-service-time:
104+
- '3261'
105+
status:
106+
code: 200
107+
message: OK
108+
version: 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
interactions:
2+
- request:
3+
body: '{"max_tokens":1024,"messages":[{"role":"user","content":"Tell me a joke
4+
about OpenTelemetry and rate it from 1 to 10"}],"model":"claude-sonnet-4-5-20250929","output_format":{"type":"json_schema","schema":{"type":"object","properties":{"joke":{"type":"string","description":"A
5+
joke about OpenTelemetry"},"rating":{"type":"integer","description":"Rating
6+
of the joke from 1 to 10"}},"required":["joke","rating"],"additionalProperties":false}}}'
7+
headers:
8+
accept:
9+
- application/json
10+
accept-encoding:
11+
- gzip, deflate
12+
anthropic-beta:
13+
- structured-outputs-2025-11-13
14+
anthropic-version:
15+
- '2023-06-01'
16+
connection:
17+
- keep-alive
18+
content-length:
19+
- '440'
20+
content-type:
21+
- application/json
22+
host:
23+
- api.anthropic.com
24+
user-agent:
25+
- Anthropic/Python 0.74.1
26+
x-stainless-arch:
27+
- arm64
28+
x-stainless-async:
29+
- 'false'
30+
x-stainless-lang:
31+
- python
32+
x-stainless-os:
33+
- MacOS
34+
x-stainless-package-version:
35+
- 0.74.1
36+
x-stainless-read-timeout:
37+
- '600'
38+
x-stainless-retry-count:
39+
- '0'
40+
x-stainless-runtime:
41+
- CPython
42+
x-stainless-runtime-version:
43+
- 3.11.7
44+
x-stainless-timeout:
45+
- '600'
46+
method: POST
47+
uri: https://api.anthropic.com/v1/messages?beta=true
48+
response:
49+
body:
50+
string: !!binary |
51+
H4sIAAAAAAAAA3SRQY/TMBCF/8owZ7dqoxZYX1ZCggNIcEFCQFDk2o/EW8cO9qRLqPLfUaqtYEF7
52+
Gmm+92aeZs7cJ4fAmm0wo8OqpBghq91qv6o21X5zU92wYu9Yc1/aZrPdurh7J7/efsaX17s2lcP7
53+
u/T8DSuWacCiQimmBSvOKSwNU4ovYqKwYpuiIArrr+erXvBzIZei+VzzXTqiZk01f+omct6RdCCH
54+
E0IakOmQYY40DnTvpaMPA+JHBPSQPN3SK1gzFiyWiSLg4KikHlQGY7Fer+kwymMT3ZtCJmQYN5Fk
55+
Y48+toQT8kTFxzZczJHS92Wqz5QRjPgUS+eHZzUrqjkb8bFdUr+Yef6muEgamgxTUmTNiK6RMUd+
56+
AAU/RkQL1nEMQfF4uZg+s4/DKI2kI2JhXVWVYmtsh8ZmXHY2jxWbK1/SP8Wu3mUBhg49sgnNvv9f
57+
/4duu3/prDiN8ndr91JxQT55i0Y8Mmte/uxMdjzPvwEAAP//AwBUTNo5WgIAAA==
58+
headers:
59+
CF-RAY:
60+
- 9a30f7d1cfdb7d9e-TLV
61+
Connection:
62+
- keep-alive
63+
Content-Encoding:
64+
- gzip
65+
Content-Type:
66+
- application/json
67+
Date:
68+
- Sun, 23 Nov 2025 13:21:12 GMT
69+
Server:
70+
- cloudflare
71+
Transfer-Encoding:
72+
- chunked
73+
X-Robots-Tag:
74+
- none
75+
anthropic-organization-id:
76+
- 617d109c-a187-4902-889d-689223d134aa
77+
anthropic-ratelimit-input-tokens-limit:
78+
- '2000000'
79+
anthropic-ratelimit-input-tokens-remaining:
80+
- '2000000'
81+
anthropic-ratelimit-input-tokens-reset:
82+
- '2025-11-23T13:21:11Z'
83+
anthropic-ratelimit-output-tokens-limit:
84+
- '400000'
85+
anthropic-ratelimit-output-tokens-remaining:
86+
- '400000'
87+
anthropic-ratelimit-output-tokens-reset:
88+
- '2025-11-23T13:21:12Z'
89+
anthropic-ratelimit-tokens-limit:
90+
- '2400000'
91+
anthropic-ratelimit-tokens-remaining:
92+
- '2400000'
93+
anthropic-ratelimit-tokens-reset:
94+
- '2025-11-23T13:21:11Z'
95+
cf-cache-status:
96+
- DYNAMIC
97+
request-id:
98+
- req_011CVQtbqszdJeNPUoVMEFqc
99+
retry-after:
100+
- '49'
101+
strict-transport-security:
102+
- max-age=31536000; includeSubDomains; preload
103+
x-envoy-upstream-service-time:
104+
- '3238'
105+
status:
106+
code: 200
107+
message: OK
108+
version: 1

0 commit comments

Comments
 (0)