Skip to content

Commit dd5dc9f

Browse files
openai: maps non-string, non-dict schema as json_schema (elastic#68)
* Maps non-string, non-dict schema as json_schema Signed-off-by: Adrian Cole <[email protected]> * elif Signed-off-by: Adrian Cole <[email protected]> * dead code Signed-off-by: Adrian Cole <[email protected]> --------- Signed-off-by: Adrian Cole <[email protected]>
1 parent 28baa58 commit dd5dc9f

File tree

5 files changed

+455
-2
lines changed

5 files changed

+455
-2
lines changed

instrumentation/elastic-opentelemetry-instrumentation-openai/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Homepage = "https://github.com/elastic/elastic-otel-python-instrumentations"
4141
"Bug Tracker" = "https://github.com/elastic/elastic-otel-python-instrumentations/issues"
4242

4343
[project.optional-dependencies]
44-
dev = ["pytest", "pip-tools", "openai", "numpy", "opentelemetry-test-utils", "vcrpy", "pytest-asyncio", "pytest-vcr"]
44+
dev = ["pytest", "pip-tools", "openai", "numpy", "opentelemetry-test-utils", "vcrpy", "pytest-asyncio", "pytest-vcr", "pydantic"]
4545
instruments = [
4646
"openai >= 1.2.0",
4747
]

instrumentation/elastic-opentelemetry-instrumentation-openai/src/opentelemetry/instrumentation/openai/helpers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,12 @@ def _is_set(value):
164164
if isinstance(response_format, Mapping):
165165
if _is_set(response_format_type := response_format.get("type")):
166166
span_attributes[GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT] = response_format_type
167-
else:
167+
elif isinstance(response_format, str):
168168
span_attributes[GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT] = response_format
169+
else:
170+
# Assume structured output lazily parsed to a schema via type_to_response_format_param or similar.
171+
# e.g. pydantic._internal._model_construction.ModelMetaclass
172+
span_attributes[GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT] = "json_schema"
169173

170174
return span_attributes
171175

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
interactions:
2+
- request:
3+
body: |-
4+
{
5+
"messages": [
6+
{
7+
"role": "user",
8+
"content": "Provide up to 3 words explaining why 2 + 2 equals 4 in JSON format with a 'reason' key."
9+
}
10+
],
11+
"model": "gpt-4o-mini",
12+
"response_format": {
13+
"type": "json_object"
14+
},
15+
"stream": false
16+
}
17+
headers:
18+
accept:
19+
- application/json
20+
accept-encoding:
21+
- gzip, deflate
22+
authorization:
23+
- Bearer test_openai_api_key
24+
connection:
25+
- keep-alive
26+
content-length:
27+
- '219'
28+
content-type:
29+
- application/json
30+
host:
31+
- api.openai.com
32+
user-agent:
33+
- OpenAI/Python 1.54.4
34+
x-stainless-arch:
35+
- arm64
36+
x-stainless-async:
37+
- 'false'
38+
x-stainless-helper-method:
39+
- beta.chat.completions.parse
40+
x-stainless-lang:
41+
- python
42+
x-stainless-os:
43+
- MacOS
44+
x-stainless-package-version:
45+
- 1.54.4
46+
x-stainless-retry-count:
47+
- '0'
48+
x-stainless-runtime:
49+
- CPython
50+
x-stainless-runtime-version:
51+
- 3.12.8
52+
method: POST
53+
uri: https://api.openai.com/v1/chat/completions
54+
response:
55+
body:
56+
string: |-
57+
{
58+
"id": "chatcmpl-BCQUdjVKJTEdMSvJ2QOiAWDrK0snY",
59+
"object": "chat.completion",
60+
"created": 1742301475,
61+
"model": "gpt-4o-mini-2024-07-18",
62+
"choices": [
63+
{
64+
"index": 0,
65+
"message": {
66+
"role": "assistant",
67+
"content": "{\n \"reason\": \"Basic arithmetic rules\"\n}",
68+
"refusal": null,
69+
"annotations": []
70+
},
71+
"logprobs": null,
72+
"finish_reason": "stop"
73+
}
74+
],
75+
"usage": {
76+
"prompt_tokens": 33,
77+
"completion_tokens": 12,
78+
"total_tokens": 45,
79+
"prompt_tokens_details": {
80+
"cached_tokens": 0,
81+
"audio_tokens": 0
82+
},
83+
"completion_tokens_details": {
84+
"reasoning_tokens": 0,
85+
"audio_tokens": 0,
86+
"accepted_prediction_tokens": 0,
87+
"rejected_prediction_tokens": 0
88+
}
89+
},
90+
"service_tier": "default",
91+
"system_fingerprint": "fp_06737a9306"
92+
}
93+
headers:
94+
CF-RAY:
95+
- 9224c8bc4fd0d6d4-IAD
96+
Connection:
97+
- keep-alive
98+
Content-Type:
99+
- application/json
100+
Date:
101+
- Tue, 18 Mar 2025 12:37:55 GMT
102+
Server:
103+
- cloudflare
104+
Set-Cookie: test_set_cookie
105+
Transfer-Encoding:
106+
- chunked
107+
X-Content-Type-Options:
108+
- nosniff
109+
access-control-expose-headers:
110+
- X-Request-ID
111+
alt-svc:
112+
- h3=":443"; ma=86400
113+
cf-cache-status:
114+
- DYNAMIC
115+
content-length:
116+
- '853'
117+
openai-organization: test_openai_org_id
118+
openai-processing-ms:
119+
- '349'
120+
openai-version:
121+
- '2020-10-01'
122+
strict-transport-security:
123+
- max-age=31536000; includeSubDomains; preload
124+
x-ratelimit-limit-requests:
125+
- '10000'
126+
x-ratelimit-limit-tokens:
127+
- '200000'
128+
x-ratelimit-remaining-requests:
129+
- '9999'
130+
x-ratelimit-remaining-tokens:
131+
- '199961'
132+
x-ratelimit-reset-requests:
133+
- 8.64s
134+
x-ratelimit-reset-tokens:
135+
- 11ms
136+
x-request-id:
137+
- req_028aaf689eb983bd5826084c8a234d93
138+
status:
139+
code: 200
140+
message: OK
141+
version: 1
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
interactions:
2+
- request:
3+
body: |-
4+
{
5+
"messages": [
6+
{
7+
"role": "user",
8+
"content": "Provide up to 3 words explaining why 2 + 2 equals 4 in JSON format with a 'reason' key."
9+
}
10+
],
11+
"model": "gpt-4o-mini",
12+
"response_format": {
13+
"type": "json_schema",
14+
"json_schema": {
15+
"schema": {
16+
"properties": {
17+
"reason": {
18+
"title": "Reason",
19+
"type": "string"
20+
}
21+
},
22+
"required": [
23+
"reason"
24+
],
25+
"title": "Reason",
26+
"type": "object",
27+
"additionalProperties": false
28+
},
29+
"name": "Reason",
30+
"strict": true
31+
}
32+
},
33+
"stream": false
34+
}
35+
headers:
36+
accept:
37+
- application/json
38+
accept-encoding:
39+
- gzip, deflate
40+
authorization:
41+
- Bearer test_openai_api_key
42+
connection:
43+
- keep-alive
44+
content-length:
45+
- '439'
46+
content-type:
47+
- application/json
48+
host:
49+
- api.openai.com
50+
user-agent:
51+
- OpenAI/Python 1.54.4
52+
x-stainless-arch:
53+
- arm64
54+
x-stainless-async:
55+
- 'false'
56+
x-stainless-helper-method:
57+
- beta.chat.completions.parse
58+
x-stainless-lang:
59+
- python
60+
x-stainless-os:
61+
- MacOS
62+
x-stainless-package-version:
63+
- 1.54.4
64+
x-stainless-retry-count:
65+
- '0'
66+
x-stainless-runtime:
67+
- CPython
68+
x-stainless-runtime-version:
69+
- 3.12.8
70+
method: POST
71+
uri: https://api.openai.com/v1/chat/completions
72+
response:
73+
body:
74+
string: |-
75+
{
76+
"id": "chatcmpl-BCQyQRM6O8IAVbMuCXPbdLjZWwJrX",
77+
"object": "chat.completion",
78+
"created": 1742303322,
79+
"model": "gpt-4o-mini-2024-07-18",
80+
"choices": [
81+
{
82+
"index": 0,
83+
"message": {
84+
"role": "assistant",
85+
"content": "{\"reason\":\"Basic arithmetic operation\"}",
86+
"refusal": null,
87+
"annotations": []
88+
},
89+
"logprobs": null,
90+
"finish_reason": "stop"
91+
}
92+
],
93+
"usage": {
94+
"prompt_tokens": 69,
95+
"completion_tokens": 8,
96+
"total_tokens": 77,
97+
"prompt_tokens_details": {
98+
"cached_tokens": 0,
99+
"audio_tokens": 0
100+
},
101+
"completion_tokens_details": {
102+
"reasoning_tokens": 0,
103+
"audio_tokens": 0,
104+
"accepted_prediction_tokens": 0,
105+
"rejected_prediction_tokens": 0
106+
}
107+
},
108+
"service_tier": "default",
109+
"system_fingerprint": "fp_3267753c5d"
110+
}
111+
headers:
112+
CF-RAY:
113+
- 9224f5d58bc4c9b0-IAD
114+
Connection:
115+
- keep-alive
116+
Content-Type:
117+
- application/json
118+
Date:
119+
- Tue, 18 Mar 2025 13:08:42 GMT
120+
Server:
121+
- cloudflare
122+
Set-Cookie: test_set_cookie
123+
Transfer-Encoding:
124+
- chunked
125+
X-Content-Type-Options:
126+
- nosniff
127+
access-control-expose-headers:
128+
- X-Request-ID
129+
alt-svc:
130+
- h3=":443"; ma=86400
131+
cf-cache-status:
132+
- DYNAMIC
133+
content-length:
134+
- '849'
135+
openai-organization: test_openai_org_id
136+
openai-processing-ms:
137+
- '367'
138+
openai-version:
139+
- '2020-10-01'
140+
strict-transport-security:
141+
- max-age=31536000; includeSubDomains; preload
142+
x-ratelimit-limit-requests:
143+
- '10000'
144+
x-ratelimit-limit-tokens:
145+
- '200000'
146+
x-ratelimit-remaining-requests:
147+
- '9999'
148+
x-ratelimit-remaining-tokens:
149+
- '199961'
150+
x-ratelimit-reset-requests:
151+
- 8.64s
152+
x-ratelimit-reset-tokens:
153+
- 11ms
154+
x-request-id:
155+
- req_1d2a78ae178add9f8c2bece1d549d000
156+
status:
157+
code: 200
158+
message: OK
159+
version: 1

0 commit comments

Comments
 (0)