Skip to content

Commit ae58c0e

Browse files
committed
fix(openai): reasoning jsons weren't stored
1 parent bdcd2fa commit ae58c0e

File tree

3 files changed

+181
-4
lines changed

3 files changed

+181
-4
lines changed

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/v1/responses_wrappers.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,15 @@ def set_data_attributes(traced_response: TracedData, span: Span):
418418
)
419419
tool_call_index += 1
420420
elif block_dict.get("type") == "reasoning":
421-
_set_span_attribute(
422-
span, f"{GEN_AI_COMPLETION}.0.reasoning", block_dict.get("summary")
423-
)
421+
reasoning_summary = block_dict.get("summary")
422+
if reasoning_summary is not None and reasoning_summary != []:
423+
if isinstance(reasoning_summary, (dict, list)):
424+
reasoning_value = json.dumps(reasoning_summary)
425+
else:
426+
reasoning_value = reasoning_summary
427+
_set_span_attribute(
428+
span, f"{GEN_AI_COMPLETION}.0.reasoning", reasoning_value
429+
)
424430
# TODO: handle other block types, in particular other calls
425431

426432

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
interactions:
2+
- request:
3+
body: '{"input": "Explain why the sky is blue", "model": "gpt-5-nano", "reasoning":
4+
{"effort": "medium", "summary": "auto"}}'
5+
headers:
6+
accept:
7+
- application/json
8+
accept-encoding:
9+
- gzip, deflate
10+
connection:
11+
- keep-alive
12+
content-length:
13+
- '117'
14+
content-type:
15+
- application/json
16+
host:
17+
- api.openai.com
18+
user-agent:
19+
- OpenAI/Python 1.99.7
20+
x-stainless-arch:
21+
- arm64
22+
x-stainless-async:
23+
- 'false'
24+
x-stainless-lang:
25+
- python
26+
x-stainless-os:
27+
- MacOS
28+
x-stainless-package-version:
29+
- 1.99.7
30+
x-stainless-read-timeout:
31+
- '600'
32+
x-stainless-retry-count:
33+
- '0'
34+
x-stainless-runtime:
35+
- CPython
36+
x-stainless-runtime-version:
37+
- 3.12.1
38+
method: POST
39+
uri: https://api.openai.com/v1/responses
40+
response:
41+
body:
42+
string: !!binary |
43+
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
80+
headers:
81+
CF-RAY:
82+
- 976ca80d3f1b7ba8-ATL
83+
Connection:
84+
- keep-alive
85+
Content-Encoding:
86+
- gzip
87+
Content-Type:
88+
- application/json
89+
Date:
90+
- Fri, 29 Aug 2025 14:15:00 GMT
91+
Server:
92+
- cloudflare
93+
Set-Cookie:
94+
- __cf_bm=4X6ZZLnfh2KwRaDYPexu3cVi.S70CP2KXHJ4o9KJm_c-1756476900-1.0.1.1-oFYXZO_g3o_KecbSI2nIucGow2iOr6n2aBduzuDb3SOG6ifghvO5vmIEARlAYRY9N6Ng7O1rjvz.3U99XuWNeImgEn7PYjU1wVinR3ok1v8;
95+
path=/; expires=Fri, 29-Aug-25 14:45:00 GMT; domain=.api.openai.com; HttpOnly;
96+
Secure; SameSite=None
97+
- _cfuvid=AhlPesKdlPTJRagqm0vfPBxmzKgdliO2M6vF4aqgF6o-1756476900286-0.0.1.1-604800000;
98+
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
99+
Transfer-Encoding:
100+
- chunked
101+
X-Content-Type-Options:
102+
- nosniff
103+
alt-svc:
104+
- h3=":443"; ma=86400
105+
cf-cache-status:
106+
- DYNAMIC
107+
openai-organization:
108+
- traceloop
109+
openai-processing-ms:
110+
- '15702'
111+
openai-project:
112+
- proj_tzz1TbPPOXaf6j9tEkVUBIAa
113+
openai-version:
114+
- '2020-10-01'
115+
strict-transport-security:
116+
- max-age=31536000; includeSubDomains; preload
117+
x-envoy-upstream-service-time:
118+
- '15705'
119+
x-ratelimit-limit-requests:
120+
- '30000'
121+
x-ratelimit-limit-tokens:
122+
- '180000000'
123+
x-ratelimit-remaining-requests:
124+
- '29999'
125+
x-ratelimit-remaining-tokens:
126+
- '179999772'
127+
x-ratelimit-reset-requests:
128+
- 2ms
129+
x-ratelimit-reset-tokens:
130+
- 0s
131+
x-request-id:
132+
- req_b7ecb107303db8e856f73c2b708f0cb2
133+
status:
134+
code: 200
135+
message: OK
136+
version: 1

packages/opentelemetry-instrumentation-openai/tests/traces/test_responses.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,41 @@ def test_responses_reasoning(instrument_legacy, span_exporter: InMemorySpanExpor
173173
assert span.attributes["gen_ai.request.reasoning_summary"] == ()
174174

175175
assert span.attributes["gen_ai.response.reasoning_effort"] == "low"
176-
assert span.attributes["gen_ai.completion.0.reasoning"] == () # reasoning summary
176+
# When reasoning summary is None/empty, the attribute should not be set
177+
assert "gen_ai.completion.0.reasoning" not in span.attributes
177178

178179
assert span.attributes["gen_ai.usage.reasoning_tokens"] > 0
180+
181+
182+
@pytest.mark.vcr
183+
@pytest.mark.skipif(not is_reasoning_supported(),
184+
reason="Reasoning is not supported in older OpenAI library versions")
185+
def test_responses_reasoning_dict_issue(instrument_legacy, span_exporter: InMemorySpanExporter,
186+
openai_client: OpenAI):
187+
"""Test for issue #3350 - reasoning dict causing invalid type warning"""
188+
openai_client.responses.create(
189+
model="gpt-5-nano",
190+
input="Explain why the sky is blue",
191+
reasoning={
192+
"effort": "medium",
193+
"summary": "auto"
194+
},
195+
)
196+
197+
spans = span_exporter.get_finished_spans()
198+
assert len(spans) == 1
199+
span = spans[0]
200+
201+
# Verify the reasoning attributes are properly set without causing warnings
202+
assert span.attributes["gen_ai.request.reasoning_effort"] == "medium"
203+
assert span.attributes["gen_ai.request.reasoning_summary"] == "auto"
204+
205+
# This should not cause an "Invalid type dict" warning and should contain serialized reasoning
206+
assert "gen_ai.completion.0.reasoning" in span.attributes
207+
# The reasoning should be serialized as JSON since it contains complex data
208+
reasoning_attr = span.attributes["gen_ai.completion.0.reasoning"]
209+
assert isinstance(reasoning_attr, str)
210+
# Should be valid JSON containing reasoning summary data
211+
import json
212+
parsed_reasoning = json.loads(reasoning_attr)
213+
assert isinstance(parsed_reasoning, (dict, list)) # Could be dict or list depending on response structure

0 commit comments

Comments
 (0)