Skip to content

Commit 5a4c151

Browse files
authored
elastic-opentelemetry-instrumentation-openai: match all the attributes traced upstream (elastic#41)
This adds support for tracing the following attributes: - GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT - GEN_AI_OPENAI_REQUEST_SEED - GEN_AI_OPENAI_REQUEST_SERVICE_TIER - GEN_AI_OPENAI_RESPONSE_SERVICE_TIER `service_tier` parameter has been added in openai 1.35.0 so skipping on old versions and only reported in the response by openai anyway.
1 parent ba14255 commit 5a4c151

10 files changed

+596
-71
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ def _chat_completion_wrapper(self, wrapped, instance, args, kwargs):
176176
logger.debug(f"openai.resources.chat.completions.Completions.create result: {result}")
177177

178178
if span.is_recording():
179-
_set_span_attributes_from_response(span, result.id, result.model, result.choices, result.usage)
179+
_set_span_attributes_from_response(
180+
span, result.id, result.model, result.choices, result.usage, getattr(result, "service_tier", None)
181+
)
180182

181183
_record_token_usage_metrics(self.token_usage_metric, span, result.usage)
182184
_record_operation_duration_metric(self.operation_duration_metric, span, start_time)
@@ -231,7 +233,9 @@ async def _async_chat_completion_wrapper(self, wrapped, instance, args, kwargs):
231233
logger.debug(f"openai.resources.chat.completions.AsyncCompletions.create result: {result}")
232234

233235
if span.is_recording():
234-
_set_span_attributes_from_response(span, result.id, result.model, result.choices, result.usage)
236+
_set_span_attributes_from_response(
237+
span, result.id, result.model, result.choices, result.usage, getattr(result, "service_tier", None)
238+
)
235239

236240
_record_token_usage_metrics(self.token_usage_metric, span, result.usage)
237241
_record_operation_duration_metric(self.operation_duration_metric, span, start_time)

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616

1717
from collections.abc import Iterable, Mapping
1818
from timeit import default_timer
19-
from typing import TYPE_CHECKING
19+
from typing import TYPE_CHECKING, Optional
2020

2121
from opentelemetry._events import Event, EventLogger
2222
from opentelemetry.semconv._incubating.attributes.gen_ai_attributes import (
23+
GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT,
24+
GEN_AI_OPENAI_REQUEST_SEED,
25+
GEN_AI_OPENAI_REQUEST_SERVICE_TIER,
26+
GEN_AI_OPENAI_RESPONSE_SERVICE_TIER,
2327
GEN_AI_OPERATION_NAME,
2428
GEN_AI_REQUEST_FREQUENCY_PENALTY,
2529
GEN_AI_REQUEST_MAX_TOKENS,
@@ -65,7 +69,12 @@
6569

6670

6771
def _set_span_attributes_from_response(
68-
span: Span, response_id: str, model: str, choices, usage: CompletionUsage
72+
span: Span,
73+
response_id: str,
74+
model: str,
75+
choices,
76+
usage: CompletionUsage,
77+
service_tier: Optional[str],
6978
) -> None:
7079
span.set_attribute(GEN_AI_RESPONSE_ID, response_id)
7180
span.set_attribute(GEN_AI_RESPONSE_MODEL, model)
@@ -76,6 +85,9 @@ def _set_span_attributes_from_response(
7685
if usage:
7786
span.set_attribute(GEN_AI_USAGE_INPUT_TOKENS, usage.prompt_tokens)
7887
span.set_attribute(GEN_AI_USAGE_OUTPUT_TOKENS, usage.completion_tokens)
88+
# this is available only if requested
89+
if service_tier:
90+
span.set_attribute(GEN_AI_OPENAI_RESPONSE_SERVICE_TIER, service_tier)
7991

8092

8193
def _set_embeddings_span_attributes_from_response(span: Span, model: str, usage: CompletionUsage) -> None:
@@ -126,6 +138,17 @@ def _get_span_attributes_from_wrapper(instance, kwargs) -> Attributes:
126138
if isinstance(stop_sequences, str):
127139
stop_sequences = [stop_sequences]
128140
span_attributes[GEN_AI_REQUEST_STOP_SEQUENCES] = stop_sequences
141+
if (seed := kwargs.get("seed")) is not None:
142+
span_attributes[GEN_AI_OPENAI_REQUEST_SEED] = seed
143+
if (service_tier := kwargs.get("service_tier")) is not None:
144+
span_attributes[GEN_AI_OPENAI_REQUEST_SERVICE_TIER] = service_tier
145+
if (response_format := kwargs.get("response_format")) is not None:
146+
# response_format may be string or object with a string in the `type` key
147+
if isinstance(response_format, Mapping):
148+
if (response_format_type := response_format.get("type")) is not None:
149+
span_attributes[GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT] = response_format_type
150+
else:
151+
span_attributes[GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT] = response_format
129152

130153
return span_attributes
131154

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def __init__(
5959
self.model = None
6060
self.choices = []
6161
self.usage = None
62+
self.service_tier = None
6263

6364
def end(self, exc=None):
6465
# StopIteration is not an error, it signals that we have consumed all the stream
@@ -70,7 +71,9 @@ def end(self, exc=None):
7071
return
7172

7273
if self.span.is_recording():
73-
_set_span_attributes_from_response(self.span, self.response_id, self.model, self.choices, self.usage)
74+
_set_span_attributes_from_response(
75+
self.span, self.response_id, self.model, self.choices, self.usage, self.service_tier
76+
)
7477

7578
_record_operation_duration_metric(self.operation_duration_metric, self.span, self.start_time)
7679
if self.usage:
@@ -92,6 +95,8 @@ def process_chunk(self, chunk):
9295
# with `include_usage` in `stream_options` we will get a last chunk without choices
9396
if chunk.choices:
9497
self.choices += chunk.choices
98+
if hasattr(chunk, "service_tier"):
99+
self.service_tier = chunk.service_tier
95100

96101
def __enter__(self):
97102
return self

instrumentation/elastic-opentelemetry-instrumentation-openai/tests/cassettes/.test_chat_completions/test_all_the_client_options[azure_provider_chat_completions].yaml

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ interactions:
1212
"frequency_penalty": 0,
1313
"max_tokens": 100,
1414
"presence_penalty": 0,
15+
"response_format": {
16+
"type": "text"
17+
},
18+
"seed": 100,
19+
"service_tier": "default",
1520
"stop": "foo",
1621
"temperature": 1,
1722
"top_p": 1
@@ -28,29 +33,29 @@ interactions:
2833
connection:
2934
- keep-alive
3035
content-length:
31-
- '244'
36+
- '321'
3237
content-type:
3338
- application/json
3439
host:
3540
- test.openai.azure.com
3641
user-agent:
37-
- AzureOpenAI/Python 1.54.3
42+
- AzureOpenAI/Python 1.54.5
3843
x-stainless-arch:
39-
- arm64
44+
- x64
4045
x-stainless-async:
4146
- 'false'
4247
x-stainless-lang:
4348
- python
4449
x-stainless-os:
45-
- MacOS
50+
- Linux
4651
x-stainless-package-version:
47-
- 1.54.3
52+
- 1.54.5
4853
x-stainless-retry-count:
4954
- '0'
5055
x-stainless-runtime:
5156
- CPython
5257
x-stainless-runtime-version:
53-
- 3.12.6
58+
- 3.10.12
5459
method: POST
5560
uri: https://test.openai.azure.com/openai/deployments/test-azure-deployment/chat/completions?api-version=2024-08-01-preview
5661
response:
@@ -64,6 +69,14 @@ interactions:
6469
"filtered": false,
6570
"severity": "safe"
6671
},
72+
"protected_material_code": {
73+
"filtered": false,
74+
"detected": false
75+
},
76+
"protected_material_text": {
77+
"filtered": false,
78+
"detected": false
79+
},
6780
"self_harm": {
6881
"filtered": false,
6982
"severity": "safe"
@@ -81,14 +94,14 @@ interactions:
8194
"index": 0,
8295
"logprobs": null,
8396
"message": {
84-
"content": "Atlantic Ocean",
97+
"content": "South Atlantic Ocean.",
8598
"role": "assistant"
8699
}
87100
}
88101
],
89-
"created": 1731466203,
90-
"id": "chatcmpl-ASxkBZGOa53uXX1Ciygl77IrF8PbB",
91-
"model": "gpt-4-32k",
102+
"created": 1733409253,
103+
"id": "chatcmpl-Ab7DhFk7vSvmMW4ICIZh0gkvTZn7G",
104+
"model": "gpt-4o-mini",
92105
"object": "chat.completion",
93106
"prompt_filter_results": [
94107
{
@@ -98,6 +111,10 @@ interactions:
98111
"filtered": false,
99112
"severity": "safe"
100113
},
114+
"jailbreak": {
115+
"filtered": false,
116+
"detected": false
117+
},
101118
"self_harm": {
102119
"filtered": false,
103120
"severity": "safe"
@@ -113,48 +130,46 @@ interactions:
113130
}
114131
}
115132
],
116-
"system_fingerprint": null,
133+
"system_fingerprint": "fp_04751d0b65",
117134
"usage": {
118-
"completion_tokens": 2,
135+
"completion_tokens": 4,
119136
"prompt_tokens": 24,
120-
"total_tokens": 26
137+
"total_tokens": 28
121138
}
122139
}
123140
headers:
124-
Cache-Control:
125-
- no-cache, must-revalidate
126141
Content-Length:
127-
- '805'
142+
- '997'
128143
Content-Type:
129144
- application/json
130145
Date:
131-
- Wed, 13 Nov 2024 02:50:02 GMT
146+
- Thu, 05 Dec 2024 14:34:13 GMT
132147
Set-Cookie: test_set_cookie
133148
Strict-Transport-Security:
134149
- max-age=31536000; includeSubDomains; preload
135-
access-control-allow-origin:
136-
- '*'
137150
apim-request-id:
138-
- f0e5ae5b-b609-4908-bedb-533ec71e9bfa
151+
- ad6ebb52-6f0c-427c-b4cd-a186597cff93
139152
azureml-model-session:
140-
- d156-20241010120317
153+
- d029-20241115170135
141154
openai-organization: test_openai_org_id
142155
x-accel-buffering:
143156
- 'no'
144157
x-content-type-options:
145158
- nosniff
159+
x-envoy-upstream-service-time:
160+
- '180'
146161
x-ms-client-request-id:
147-
- f0e5ae5b-b609-4908-bedb-533ec71e9bfa
162+
- ad6ebb52-6f0c-427c-b4cd-a186597cff93
148163
x-ms-rai-invoked:
149164
- 'true'
150165
x-ms-region:
151-
- Switzerland North
166+
- East US
152167
x-ratelimit-remaining-requests:
153-
- '78'
168+
- '909'
154169
x-ratelimit-remaining-tokens:
155-
- '79884'
170+
- '90883'
156171
x-request-id:
157-
- 3a1ee803-cce9-472f-ad04-2d0757009288
172+
- 80dd2ee4-7ce2-4d04-a114-efb137a58ed4
158173
status:
159174
code: 200
160175
message: OK

instrumentation/elastic-opentelemetry-instrumentation-openai/tests/cassettes/.test_chat_completions/test_all_the_client_options[ollama_provider_chat_completions].yaml

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ interactions:
1212
"frequency_penalty": 0,
1313
"max_tokens": 100,
1414
"presence_penalty": 0,
15+
"response_format": {
16+
"type": "text"
17+
},
18+
"seed": 100,
19+
"service_tier": "default",
1520
"stop": "foo",
1621
"temperature": 1,
1722
"top_p": 1
@@ -26,65 +31,65 @@ interactions:
2631
connection:
2732
- keep-alive
2833
content-length:
29-
- '250'
34+
- '327'
3035
content-type:
3136
- application/json
3237
host:
3338
- localhost:11434
3439
user-agent:
35-
- OpenAI/Python 1.50.2
40+
- OpenAI/Python 1.54.5
3641
x-stainless-arch:
37-
- arm64
42+
- x64
3843
x-stainless-async:
3944
- 'false'
4045
x-stainless-lang:
4146
- python
4247
x-stainless-os:
43-
- MacOS
48+
- Linux
4449
x-stainless-package-version:
45-
- 1.50.2
50+
- 1.54.5
4651
x-stainless-retry-count:
4752
- '0'
4853
x-stainless-runtime:
4954
- CPython
5055
x-stainless-runtime-version:
51-
- 3.12.6
56+
- 3.10.12
5257
method: POST
5358
uri: http://localhost:11434/v1/chat/completions
5459
response:
5560
body:
5661
string: |-
5762
{
58-
"id": "chatcmpl-46",
63+
"id": "chatcmpl-593",
5964
"object": "chat.completion",
60-
"created": 1731311779,
65+
"created": 1733409255,
6166
"model": "qwen2.5:0.5b",
6267
"system_fingerprint": "fp_ollama",
6368
"choices": [
6469
{
6570
"index": 0,
6671
"message": {
6772
"role": "assistant",
68-
"content": "The Falklands Islands are located in Atlantic Oceans."
73+
"content": "Amalfis Sea"
6974
},
7075
"finish_reason": "stop"
7176
}
7277
],
7378
"usage": {
7479
"prompt_tokens": 46,
75-
"completion_tokens": 12,
76-
"total_tokens": 58
80+
"completion_tokens": 5,
81+
"total_tokens": 51
7782
}
7883
}
7984
headers:
8085
Content-Length:
81-
- '339'
86+
- '297'
8287
Content-Type:
8388
- application/json
8489
Date:
85-
- Mon, 11 Nov 2024 07:56:19 GMT
90+
- Thu, 05 Dec 2024 14:34:15 GMT
8691
Set-Cookie: test_set_cookie
87-
openai-organization: test_openai_org_key
92+
openai-organization: test_openai_org_id
8893
status:
8994
code: 200
9095
message: OK

0 commit comments

Comments
 (0)