Skip to content

Commit 1efdfcd

Browse files
committed
addressing comments:
1. clean up prompt calculations to be deduced from last streaming chunk 2. save correct span name 3. remove recording exceptions and setting status to ok 4. remove saving stream chunks in events
1 parent e15d443 commit 1efdfcd

File tree

1 file changed

+33
-95
lines changed
  • instrumentation/opentelemetry-instrumentation-openai/src/opentelemetry/instrumentation/openai

1 file changed

+33
-95
lines changed

Diff for: instrumentation/opentelemetry-instrumentation-openai/src/opentelemetry/instrumentation/openai/patch.py

+33-95
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,19 @@
1313
# limitations under the License.
1414

1515
import json
16+
from typing import Optional, Union
1617
from opentelemetry import trace
1718
from opentelemetry.trace import SpanKind, Span
1819
from opentelemetry.trace.status import Status, StatusCode
1920
from opentelemetry.trace.propagation import set_span_in_context
20-
from openai._types import NOT_GIVEN
21-
from span_attributes import SpanAttributes, LLMSpanAttributes, Event
22-
from utils import (
23-
estimate_tokens,
24-
silently_fail,
25-
extract_content,
26-
calculate_prompt_tokens,
27-
)
21+
from openai import NOT_GIVEN
22+
from .span_attributes import LLMSpanAttributes, SpanAttributes
2823

24+
from .utils import silently_fail, extract_content
25+
from opentelemetry.trace import Tracer
2926

30-
def chat_completions_create(original_method, version, tracer):
27+
28+
def chat_completions_create(original_method, version, tracer: Tracer):
3129
"""Wrap the `create` method of the `ChatCompletion` class to trace it."""
3230

3331
def traced_method(wrapped, instance, args, kwargs):
@@ -69,8 +67,10 @@ def traced_method(wrapped, instance, args, kwargs):
6967

7068
attributes = LLMSpanAttributes(**span_attributes)
7169

70+
span_name = f"{attributes.gen_ai_operation_name} {attributes.gen_ai_request_model}"
71+
7272
span = tracer.start_span(
73-
"openai.completion",
73+
name=span_name,
7474
kind=SpanKind.CLIENT,
7575
context=set_span_in_context(trace.get_current_span()),
7676
)
@@ -79,36 +79,18 @@ def traced_method(wrapped, instance, args, kwargs):
7979
try:
8080
result = wrapped(*args, **kwargs)
8181
if is_streaming(kwargs):
82-
prompt_tokens = 0
83-
for message in kwargs.get("messages", {}):
84-
prompt_tokens += calculate_prompt_tokens(
85-
json.dumps(str(message)), kwargs.get("model")
86-
)
87-
88-
if (
89-
kwargs.get("functions") is not None
90-
and kwargs.get("functions") != NOT_GIVEN
91-
):
92-
for function in kwargs.get("functions"):
93-
prompt_tokens += calculate_prompt_tokens(
94-
json.dumps(function), kwargs.get("model")
95-
)
96-
9782
return StreamWrapper(
9883
result,
9984
span,
100-
prompt_tokens,
10185
function_call=kwargs.get("functions") is not None,
10286
tool_calls=kwargs.get("tools") is not None,
10387
)
10488
else:
10589
_set_response_attributes(span, kwargs, result)
106-
span.set_status(StatusCode.OK)
10790
span.end()
10891
return result
10992

11093
except Exception as error:
111-
span.record_exception(error)
11294
span.set_status(Status(StatusCode.ERROR, str(error)))
11395
span.end()
11496
raise
@@ -118,21 +100,14 @@ def traced_method(wrapped, instance, args, kwargs):
118100

119101
def get_tool_calls(item):
120102
if isinstance(item, dict):
121-
if "tool_calls" in item and item["tool_calls"] is not None:
122-
return item["tool_calls"]
123-
return None
124-
103+
return item.get("tool_calls")
125104
else:
126-
if hasattr(item, "tool_calls") and item.tool_calls is not None:
127-
return item.tool_calls
128-
return None
105+
return getattr(item, "tool_calls", None)
129106

130107

131108
@silently_fail
132-
def _set_input_attributes(span, kwargs, attributes):
109+
def _set_input_attributes(span, kwargs, attributes: LLMSpanAttributes):
133110
tools = []
134-
for field, value in attributes.model_dump(by_alias=True).items():
135-
set_span_attribute(span, field, value)
136111

137112
if (
138113
kwargs.get("functions") is not None
@@ -149,6 +124,9 @@ def _set_input_attributes(span, kwargs, attributes):
149124
if tools:
150125
set_span_attribute(span, SpanAttributes.LLM_TOOLS, json.dumps(tools))
151126

127+
for field, value in attributes.model_dump(by_alias=True).items():
128+
set_span_attribute(span, field, value)
129+
152130

153131
@silently_fail
154132
def _set_response_attributes(span, kwargs, result):
@@ -230,15 +208,6 @@ def set_event_completion(span: Span, result_content):
230208
)
231209

232210

233-
def set_event_completion_chunk(span: Span, chunk):
234-
span.add_event(
235-
name=SpanAttributes.LLM_CONTENT_COMPLETION_CHUNK,
236-
attributes={
237-
SpanAttributes.LLM_CONTENT_COMPLETION_CHUNK: json.dumps(chunk),
238-
},
239-
)
240-
241-
242211
def set_span_attribute(span: Span, name, value):
243212
if value is not None:
244213
if value != "" or value != NOT_GIVEN:
@@ -250,33 +219,33 @@ def set_span_attribute(span: Span, name, value):
250219

251220

252221
def is_streaming(kwargs):
253-
return not (
254-
kwargs.get("stream") is False
255-
or kwargs.get("stream") is None
256-
or kwargs.get("stream") == NOT_GIVEN
257-
)
222+
return non_numerical_value_is_set(kwargs.get("stream"))
223+
224+
225+
def non_numerical_value_is_set(value: Optional[Union[bool, str]]):
226+
return bool(value) and value != NOT_GIVEN
258227

259228

260229
def get_llm_request_attributes(
261230
kwargs, prompts=None, model=None, operation_name="chat"
262231
):
263232

264-
user = kwargs.get("user", None)
233+
user = kwargs.get("user")
265234
if prompts is None:
266235
prompts = (
267236
[{"role": user or "user", "content": kwargs.get("prompt")}]
268237
if "prompt" in kwargs
269238
else None
270239
)
271240
top_k = (
272-
kwargs.get("n", None)
273-
or kwargs.get("k", None)
274-
or kwargs.get("top_k", None)
275-
or kwargs.get("top_n", None)
241+
kwargs.get("n")
242+
or kwargs.get("k")
243+
or kwargs.get("top_k")
244+
or kwargs.get("top_n")
276245
)
277246

278-
top_p = kwargs.get("p", None) or kwargs.get("top_p", None)
279-
tools = kwargs.get("tools", None)
247+
top_p = kwargs.get("p") or kwargs.get("top_p")
248+
tools = kwargs.get("tools")
280249
return {
281250
SpanAttributes.LLM_OPERATION_NAME: operation_name,
282251
SpanAttributes.LLM_REQUEST_MODEL: model or kwargs.get("model"),
@@ -308,7 +277,7 @@ def __init__(
308277
self,
309278
stream,
310279
span,
311-
prompt_tokens,
280+
prompt_tokens=None,
312281
function_call=False,
313282
tool_calls=False,
314283
):
@@ -324,12 +293,10 @@ def __init__(
324293

325294
def setup(self):
326295
if not self._span_started:
327-
self.span.add_event(Event.STREAM_START.value)
328296
self._span_started = True
329297

330298
def cleanup(self):
331299
if self._span_started:
332-
self.span.add_event(Event.STREAM_END.value)
333300
set_span_attribute(
334301
self.span,
335302
SpanAttributes.LLM_USAGE_PROMPT_TOKENS,
@@ -391,8 +358,6 @@ def process_chunk(self, chunk):
391358
if not self.function_call and not self.tool_calls:
392359
for choice in chunk.choices:
393360
if choice.delta and choice.delta.content is not None:
394-
token_counts = estimate_tokens(choice.delta.content)
395-
self.completion_tokens += token_counts
396361
content = [choice.delta.content]
397362
elif self.function_call:
398363
for choice in chunk.choices:
@@ -401,10 +366,6 @@ def process_chunk(self, chunk):
401366
and choice.delta.function_call is not None
402367
and choice.delta.function_call.arguments is not None
403368
):
404-
token_counts = estimate_tokens(
405-
choice.delta.function_call.arguments
406-
)
407-
self.completion_tokens += token_counts
408369
content = [choice.delta.function_call.arguments]
409370
elif self.tool_calls:
410371
for choice in chunk.choices:
@@ -417,40 +378,17 @@ def process_chunk(self, chunk):
417378
and tool_call.function is not None
418379
and tool_call.function.arguments is not None
419380
):
420-
token_counts = estimate_tokens(
421-
tool_call.function.arguments
422-
)
423-
self.completion_tokens += token_counts
424381
content.append(tool_call.function.arguments)
425-
set_event_completion_chunk(
426-
self.span,
427-
(
428-
"".join(content)
429-
if len(content) > 0 and content[0] is not None
430-
else ""
431-
),
432-
)
382+
433383
if content:
434384
self.result_content.append(content[0])
435385

436386
if hasattr(chunk, "text"):
437-
token_counts = estimate_tokens(chunk.text)
438-
self.completion_tokens += token_counts
439387
content = [chunk.text]
440-
set_event_completion_chunk(
441-
self.span,
442-
(
443-
"".join(content)
444-
if len(content) > 0 and content[0] is not None
445-
else ""
446-
),
447-
)
448388

449389
if content:
450390
self.result_content.append(content[0])
451391

452-
if hasattr(chunk, "usage_metadata"):
453-
self.completion_tokens = (
454-
chunk.usage_metadata.candidates_token_count
455-
)
456-
self.prompt_tokens = chunk.usage_metadata.prompt_token_count
392+
if getattr(chunk, "usage"):
393+
self.completion_tokens = chunk.usage.completion_tokens
394+
self.prompt_tokens = chunk.usage.prompt_tokens

0 commit comments

Comments
 (0)