Skip to content

Commit 237e2fd

Browse files
authored
elastic-opentelemetry-instrumentation-openai: fix tracing of openai client optional params (#12)
Fix some precedence bugs when settings optional client attributes, before the fixes all the values had `true` as value.
1 parent 9e6e917 commit 237e2fd

File tree

3 files changed

+166
-6
lines changed

3 files changed

+166
-6
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,17 @@ def _get_span_attributes_from_wrapper(instance, kwargs):
112112
elif scheme == "https":
113113
span_attributes[SERVER_PORT] = 443
114114

115-
if frequency_penalty := kwargs.get("frequency_penalty") is not None:
115+
if (frequency_penalty := kwargs.get("frequency_penalty")) is not None:
116116
span_attributes[GEN_AI_REQUEST_FREQUENCY_PENALTY] = frequency_penalty
117-
if max_tokens := kwargs.get("max_tokens") is not None:
117+
if (max_tokens := kwargs.get("max_completion_tokens", kwargs.get("max_tokens"))) is not None:
118118
span_attributes[GEN_AI_REQUEST_MAX_TOKENS] = max_tokens
119-
if presence_penalty := kwargs.get("presence_penalty") is not None:
119+
if (presence_penalty := kwargs.get("presence_penalty")) is not None:
120120
span_attributes[GEN_AI_REQUEST_PRESENCE_PENALTY] = presence_penalty
121-
if temperature := kwargs.get("temperature") is not None:
121+
if (temperature := kwargs.get("temperature")) is not None:
122122
span_attributes[GEN_AI_REQUEST_TEMPERATURE] = temperature
123-
if top_p := kwargs.get("top_p") is not None:
123+
if (top_p := kwargs.get("top_p")) is not None:
124124
span_attributes[GEN_AI_REQUEST_TOP_P] = top_p
125-
if stop_sequences := kwargs.get("stop") is not None:
125+
if (stop_sequences := kwargs.get("stop")) is not None:
126126
if isinstance(stop_sequences, str):
127127
stop_sequences = [stop_sequences]
128128
span_attributes[GEN_AI_REQUEST_STOP_SEQUENCES] = stop_sequences
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
interactions:
2+
- request:
3+
body: '{"messages": [{"role": "user", "content": "Answer in up to 3 words: Which
4+
ocean contains the falkland islands?"}], "model": "gpt-4o-mini", "frequency_penalty":
5+
0, "max_completion_tokens": 100, "presence_penalty": 0, "stop": "foo", "temperature":
6+
1, "top_p": 1}'
7+
headers:
8+
accept:
9+
- application/json
10+
accept-encoding:
11+
- gzip, deflate
12+
authorization:
13+
- Bearer test_openai_api_key
14+
connection:
15+
- keep-alive
16+
content-length:
17+
- '260'
18+
content-type:
19+
- application/json
20+
host:
21+
- api.openai.com
22+
user-agent:
23+
- OpenAI/Python 1.50.2
24+
x-stainless-arch:
25+
- x64
26+
x-stainless-async:
27+
- 'false'
28+
x-stainless-lang:
29+
- python
30+
x-stainless-os:
31+
- Linux
32+
x-stainless-package-version:
33+
- 1.50.2
34+
x-stainless-retry-count:
35+
- '0'
36+
x-stainless-runtime:
37+
- CPython
38+
x-stainless-runtime-version:
39+
- 3.10.12
40+
method: POST
41+
uri: https://api.openai.com/v1/chat/completions
42+
response:
43+
body:
44+
string: !!binary |
45+
H4sIAAAAAAAAA3SQMU/DMBCF9/wKy3NTpSE0IVslxAZUqoABoch1LompYxv7QoGq/x05aZMysHh4
46+
373nd3cICKGipDmhvGHIWyPD1e1TWS8X6/3Lh7q6W6vk+WH3k3zu73ebZUxn3qG378Dx7Jpz3RoJ
47+
KLQaMLfAEHzqIo3TNL1JoqQHrS5BelttMEx02AolwjiKkzBKw0V2cjdacHA0J68BIYQc+tf3VCV8
48+
0ZxEs7PSgnOsBpqPQ4RQq6VXKHNOOGQK6WyCXCsE1Vff6A4bskLJFApOHjkwNb+ctVB1jvm+qpPy
49+
pB/Hz6WujdVbd+KjXgklXFNYYE4r/5FDbWhPjwEhb/2S3Z/e1FjdGixQ70D5wDgZ4uh02gmeGWpk
50+
8sKT/ecpSkAmpLu4Ex3qCVVPAdHYsV+Sum+H0BaVUDVYY8VwtsoUVXa9BbZMs4QGx+AXAAD//wMA
51+
sMwiB0QCAAA=
52+
headers:
53+
CF-Cache-Status:
54+
- DYNAMIC
55+
CF-RAY:
56+
- 8cbbd9faeab95a0d-MXP
57+
Connection:
58+
- keep-alive
59+
Content-Encoding:
60+
- gzip
61+
Content-Type:
62+
- application/json
63+
Date:
64+
- Tue, 01 Oct 2024 10:43:24 GMT
65+
Server:
66+
- cloudflare
67+
Set-Cookie: test_set_cookie
68+
Transfer-Encoding:
69+
- chunked
70+
X-Content-Type-Options:
71+
- nosniff
72+
access-control-expose-headers:
73+
- X-Request-ID
74+
openai-organization: test_openai_org_key
75+
openai-processing-ms:
76+
- '145'
77+
openai-version:
78+
- '2020-10-01'
79+
strict-transport-security:
80+
- max-age=31536000; includeSubDomains; preload
81+
x-ratelimit-limit-requests:
82+
- '10000'
83+
x-ratelimit-limit-tokens:
84+
- '200000'
85+
x-ratelimit-remaining-requests:
86+
- '9999'
87+
x-ratelimit-remaining-tokens:
88+
- '199966'
89+
x-ratelimit-reset-requests:
90+
- 8.64s
91+
x-ratelimit-reset-tokens:
92+
- 10ms
93+
x-request-id:
94+
- req_b2c77a93a9995fac4529c05464b89f7e
95+
status:
96+
code: 200
97+
message: OK
98+
version: 1

instrumentation/elastic-opentelemetry-instrumentation-openai/tests/test_chat_completions.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
from opentelemetry.trace import SpanKind, StatusCode
2626
from opentelemetry.semconv._incubating.attributes.gen_ai_attributes import (
2727
GEN_AI_OPERATION_NAME,
28+
GEN_AI_REQUEST_FREQUENCY_PENALTY,
29+
GEN_AI_REQUEST_MAX_TOKENS,
2830
GEN_AI_REQUEST_MODEL,
31+
GEN_AI_REQUEST_PRESENCE_PENALTY,
32+
GEN_AI_REQUEST_STOP_SEQUENCES,
33+
GEN_AI_REQUEST_TEMPERATURE,
34+
GEN_AI_REQUEST_TOP_P,
2935
GEN_AI_SYSTEM,
3036
GEN_AI_RESPONSE_ID,
3137
GEN_AI_RESPONSE_MODEL,
@@ -222,6 +228,62 @@ def test_basic(self):
222228
self.assertOperationDurationMetric(operation_duration_metric)
223229
self.assertTokenUsageMetric(token_usage_metric)
224230

231+
def test_all_the_client_options(self):
232+
messages = [
233+
{
234+
"role": "user",
235+
"content": "Answer in up to 3 words: Which ocean contains the falkland islands?",
236+
}
237+
]
238+
239+
chat_completion = self.client.chat.completions.create(
240+
model=OPENAI_TOOL_MODEL,
241+
messages=messages,
242+
frequency_penalty=0,
243+
max_completion_tokens=100,
244+
presence_penalty=0,
245+
temperature=1,
246+
top_p=1,
247+
stop="foo",
248+
)
249+
250+
self.assertEqual(chat_completion.choices[0].message.content, "South Atlantic Ocean.")
251+
252+
spans = self.get_finished_spans()
253+
self.assertEqual(len(spans), 1)
254+
255+
span = spans[0]
256+
self.assertEqual(span.name, f"chat {OPENAI_TOOL_MODEL}")
257+
self.assertEqual(span.kind, SpanKind.CLIENT)
258+
self.assertEqual(span.status.status_code, StatusCode.UNSET)
259+
260+
self.assertEqual(
261+
dict(span.attributes),
262+
{
263+
GEN_AI_OPERATION_NAME: "chat",
264+
GEN_AI_REQUEST_FREQUENCY_PENALTY: 0,
265+
GEN_AI_REQUEST_MAX_TOKENS: 100,
266+
GEN_AI_REQUEST_MODEL: OPENAI_TOOL_MODEL,
267+
GEN_AI_REQUEST_PRESENCE_PENALTY: 0,
268+
GEN_AI_REQUEST_STOP_SEQUENCES: ("foo",),
269+
GEN_AI_REQUEST_TEMPERATURE: 1,
270+
GEN_AI_REQUEST_TOP_P: 1,
271+
GEN_AI_SYSTEM: "openai",
272+
GEN_AI_RESPONSE_ID: "chatcmpl-ADUdg61PwWqn3FPn4VNkz4vwMkS62",
273+
GEN_AI_RESPONSE_MODEL: OPENAI_TOOL_MODEL + "-2024-07-18", # Note it is more specific than request!,
274+
GEN_AI_RESPONSE_FINISH_REASONS: ("stop",),
275+
GEN_AI_USAGE_INPUT_TOKENS: 24,
276+
GEN_AI_USAGE_OUTPUT_TOKENS: 4,
277+
SERVER_ADDRESS: "api.openai.com",
278+
SERVER_PORT: 443,
279+
},
280+
)
281+
self.assertEqual(span.events, ())
282+
283+
operation_duration_metric, token_usage_metric = self.get_sorted_metrics()
284+
self.assertOperationDurationMetric(operation_duration_metric)
285+
self.assertTokenUsageMetric(token_usage_metric)
286+
225287
def test_function_calling_with_tools(self):
226288
tools = [
227289
{

0 commit comments

Comments
 (0)