Skip to content

Commit f0ca0a6

Browse files
authored
openai: handle message with developer role (elastic#58)
Send an system event with an explicit role attribute set to developer in the body
1 parent b8f038f commit f0ca0a6

File tree

3 files changed

+213
-1
lines changed

3 files changed

+213
-1
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ def _send_log_events_from_messages(
255255
content = message.get("content")
256256
if content:
257257
body["content"] = content
258-
if message["role"] == "system":
258+
if message["role"] == "system" or message["role"] == "developer":
259+
if message["role"] == "developer":
260+
body["role"] = message["role"]
259261
event = Event(name=EVENT_GEN_AI_SYSTEM_MESSAGE, body=body, attributes=attributes)
260262
event_logger.emit(event)
261263
elif message["role"] == "user":
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
interactions:
2+
- request:
3+
body: |-
4+
{
5+
"messages": [
6+
{
7+
"role": "developer",
8+
"content": "You are a friendly assistant"
9+
},
10+
{
11+
"role": "user",
12+
"content": "Answer in up to 3 words: Which ocean contains Bouvet Island?"
13+
}
14+
],
15+
"model": "gpt-4o-mini"
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+
- '197'
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+
- x64
36+
x-stainless-async:
37+
- 'false'
38+
x-stainless-lang:
39+
- python
40+
x-stainless-os:
41+
- Linux
42+
x-stainless-package-version:
43+
- 1.54.4
44+
x-stainless-retry-count:
45+
- '0'
46+
x-stainless-runtime:
47+
- CPython
48+
x-stainless-runtime-version:
49+
- 3.12.8
50+
method: POST
51+
uri: https://api.openai.com/v1/chat/completions
52+
response:
53+
body:
54+
string: |-
55+
{
56+
"id": "chatcmpl-B6vdHtqgT6rj4cj7itn9bNlaUlqHg",
57+
"object": "chat.completion",
58+
"created": 1740991207,
59+
"model": "gpt-4o-mini-2024-07-18",
60+
"choices": [
61+
{
62+
"index": 0,
63+
"message": {
64+
"role": "assistant",
65+
"content": "Atlantic Ocean.",
66+
"refusal": null
67+
},
68+
"logprobs": null,
69+
"finish_reason": "stop"
70+
}
71+
],
72+
"usage": {
73+
"prompt_tokens": 31,
74+
"completion_tokens": 4,
75+
"total_tokens": 35,
76+
"prompt_tokens_details": {
77+
"cached_tokens": 0,
78+
"audio_tokens": 0
79+
},
80+
"completion_tokens_details": {
81+
"reasoning_tokens": 0,
82+
"audio_tokens": 0,
83+
"accepted_prediction_tokens": 0,
84+
"rejected_prediction_tokens": 0
85+
}
86+
},
87+
"service_tier": "default",
88+
"system_fingerprint": "fp_06737a9306"
89+
}
90+
headers:
91+
CF-RAY:
92+
- 91a7d3c46d41eda3-MXP
93+
Connection:
94+
- keep-alive
95+
Content-Type:
96+
- application/json
97+
Date:
98+
- Mon, 03 Mar 2025 08:40:08 GMT
99+
Server:
100+
- cloudflare
101+
Set-Cookie: test_set_cookie
102+
Transfer-Encoding:
103+
- chunked
104+
X-Content-Type-Options:
105+
- nosniff
106+
access-control-expose-headers:
107+
- X-Request-ID
108+
alt-svc:
109+
- h3=":443"; ma=86400
110+
cf-cache-status:
111+
- DYNAMIC
112+
content-length:
113+
- '794'
114+
openai-organization: test_openai_org_id
115+
openai-processing-ms:
116+
- '169'
117+
openai-version:
118+
- '2020-10-01'
119+
strict-transport-security:
120+
- max-age=31536000; includeSubDomains; preload
121+
x-ratelimit-limit-requests:
122+
- '10000'
123+
x-ratelimit-limit-tokens:
124+
- '200000'
125+
x-ratelimit-remaining-requests:
126+
- '9999'
127+
x-ratelimit-remaining-tokens:
128+
- '199960'
129+
x-ratelimit-reset-requests:
130+
- 8.64s
131+
x-ratelimit-reset-tokens:
132+
- 12ms
133+
x-request-id:
134+
- req_70b661026c20278c4d79838fbba06bde
135+
status:
136+
code: 200
137+
message: OK
138+
version: 1

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,78 @@ def test_chat(default_openai_env, trace_exporter, metrics_reader, logs_exporter)
130130
)
131131

132132

133+
@pytest.mark.vcr()
134+
def test_chat_with_developer_role_message(default_openai_env, trace_exporter, metrics_reader, logs_exporter):
135+
client = openai.OpenAI()
136+
137+
messages = [
138+
{
139+
"role": "developer",
140+
"content": "You are a friendly assistant",
141+
},
142+
{
143+
"role": "user",
144+
"content": TEST_CHAT_INPUT,
145+
},
146+
]
147+
148+
chat_completion = client.chat.completions.create(model=TEST_CHAT_MODEL, messages=messages)
149+
150+
assert chat_completion.choices[0].message.content == "Atlantic Ocean."
151+
152+
spans = trace_exporter.get_finished_spans()
153+
assert len(spans) == 1
154+
155+
span = spans[0]
156+
assert span.name == f"chat {TEST_CHAT_MODEL}"
157+
assert span.kind == SpanKind.CLIENT
158+
assert span.status.status_code == StatusCode.UNSET
159+
160+
address, port = address_and_port(client)
161+
assert dict(span.attributes) == {
162+
GEN_AI_OPENAI_RESPONSE_SERVICE_TIER: "default",
163+
GEN_AI_OPERATION_NAME: "chat",
164+
GEN_AI_REQUEST_MODEL: TEST_CHAT_MODEL,
165+
GEN_AI_SYSTEM: "openai",
166+
GEN_AI_RESPONSE_ID: "chatcmpl-B6vdHtqgT6rj4cj7itn9bNlaUlqHg",
167+
GEN_AI_RESPONSE_MODEL: TEST_CHAT_RESPONSE_MODEL,
168+
GEN_AI_RESPONSE_FINISH_REASONS: ("stop",),
169+
GEN_AI_USAGE_INPUT_TOKENS: 31,
170+
GEN_AI_USAGE_OUTPUT_TOKENS: 4,
171+
SERVER_ADDRESS: address,
172+
SERVER_PORT: port,
173+
}
174+
175+
logs = logs_exporter.get_finished_logs()
176+
assert len(logs) == 3
177+
log_records = logrecords_from_logs(logs)
178+
system_message, user_message, choice = log_records
179+
assert dict(system_message.attributes) == {"gen_ai.system": "openai", "event.name": "gen_ai.system.message"}
180+
assert dict(system_message.body) == {"role": "developer"}
181+
182+
assert dict(user_message.attributes) == {"gen_ai.system": "openai", "event.name": "gen_ai.user.message"}
183+
assert dict(user_message.body) == {}
184+
185+
assert_stop_log_record(choice)
186+
187+
operation_duration_metric, token_usage_metric = get_sorted_metrics(metrics_reader)
188+
attributes = {
189+
GEN_AI_REQUEST_MODEL: TEST_CHAT_MODEL,
190+
GEN_AI_RESPONSE_MODEL: "gpt-4o-mini-2024-07-18",
191+
}
192+
assert_operation_duration_metric(
193+
client, "chat", operation_duration_metric, attributes=attributes, min_data_point=0.006761051714420319
194+
)
195+
assert_token_usage_metric(
196+
client,
197+
"chat",
198+
token_usage_metric,
199+
attributes=attributes,
200+
input_data_point=span.attributes[GEN_AI_USAGE_INPUT_TOKENS],
201+
output_data_point=span.attributes[GEN_AI_USAGE_OUTPUT_TOKENS],
202+
)
203+
204+
133205
@pytest.mark.skipif(OPENAI_VERSION < (1, 35, 0), reason="service tier added in 1.35.0")
134206
@pytest.mark.vcr()
135207
def test_chat_all_the_client_options(default_openai_env, trace_exporter, metrics_reader, logs_exporter):

0 commit comments

Comments
 (0)