Skip to content

Commit 8176494

Browse files
fix(instrumentation): remove param enrich_token_usage and simplify token calculation (#3205)
Co-authored-by: Nir Gazit <[email protected]>
1 parent 168236a commit 8176494

File tree

23 files changed

+1311
-1539
lines changed

23 files changed

+1311
-1539
lines changed

packages/opentelemetry-instrumentation-groq/opentelemetry/instrumentation/groq/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,14 +385,12 @@ class GroqInstrumentor(BaseInstrumentor):
385385

386386
def __init__(
387387
self,
388-
enrich_token_usage: bool = False,
389388
exception_logger=None,
390389
use_legacy_attributes: bool = True,
391390
get_common_metrics_attributes: Callable[[], dict] = lambda: {},
392391
):
393392
super().__init__()
394393
Config.exception_logger = exception_logger
395-
Config.enrich_token_usage = enrich_token_usage
396394
Config.get_common_metrics_attributes = get_common_metrics_attributes
397395
Config.use_legacy_attributes = use_legacy_attributes
398396

packages/opentelemetry-instrumentation-groq/opentelemetry/instrumentation/groq/config.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33

44
class Config:
5-
enrich_token_usage = False
65
exception_logger = None
76
get_common_metrics_attributes: Callable[[], dict] = lambda: {}
87
use_legacy_attributes = True

packages/opentelemetry-instrumentation-groq/tests/traces/conftest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def async_groq_client():
8383

8484
@pytest.fixture(scope="function")
8585
def instrument_legacy(reader, tracer_provider, meter_provider):
86-
instrumentor = GroqInstrumentor(enrich_token_usage=True)
86+
instrumentor = GroqInstrumentor()
8787
instrumentor.instrument(
8888
tracer_provider=tracer_provider,
8989
meter_provider=meter_provider,
@@ -102,7 +102,6 @@ def instrument_with_content(
102102

103103
instrumentor = GroqInstrumentor(
104104
use_legacy_attributes=False,
105-
enrich_token_usage=True,
106105
)
107106
instrumentor.instrument(
108107
tracer_provider=tracer_provider,
@@ -123,7 +122,7 @@ def instrument_with_no_content(
123122
os.environ.update({TRACELOOP_TRACE_CONTENT: "False"})
124123

125124
instrumentor = GroqInstrumentor(
126-
use_legacy_attributes=False, enrich_token_usage=True
125+
use_legacy_attributes=False
127126
)
128127
instrumentor.instrument(
129128
tracer_provider=tracer_provider,

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class OpenAIInstrumentor(BaseInstrumentor):
1414
def __init__(
1515
self,
1616
enrich_assistant: bool = False,
17-
enrich_token_usage: bool = False,
1817
exception_logger=None,
1918
get_common_metrics_attributes: Callable[[], dict] = lambda: {},
2019
upload_base64_image: Optional[
@@ -25,7 +24,6 @@ def __init__(
2524
):
2625
super().__init__()
2726
Config.enrich_assistant = enrich_assistant
28-
Config.enrich_token_usage = enrich_token_usage
2927
Config.exception_logger = exception_logger
3028
Config.get_common_metrics_attributes = get_common_metrics_attributes
3129
Config.upload_base64_image = upload_base64_image

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from opentelemetry.instrumentation.openai.utils import (
88
dont_throw,
99
is_openai_v1,
10-
should_record_stream_token_usage,
1110
)
1211
from opentelemetry.semconv._incubating.attributes.gen_ai_attributes import (
1312
GEN_AI_RESPONSE_ID,
@@ -24,8 +23,6 @@
2423

2524
_PYDANTIC_VERSION = version("pydantic")
2625

27-
# tiktoken encodings map for different model, key is model_name, value is tiktoken encoding
28-
tiktoken_encodings = {}
2926

3027
logger = logging.getLogger(__name__)
3128

@@ -355,36 +352,6 @@ def model_as_dict(model):
355352
return model
356353

357354

358-
def get_token_count_from_string(string: str, model_name: str):
359-
if not should_record_stream_token_usage():
360-
return None
361-
362-
import tiktoken
363-
364-
if tiktoken_encodings.get(model_name) is None:
365-
try:
366-
encoding = tiktoken.encoding_for_model(model_name)
367-
except KeyError as ex:
368-
# no such model_name in tiktoken
369-
logger.warning(
370-
f"Failed to get tiktoken encoding for model_name {model_name}, error: {str(ex)}"
371-
)
372-
return None
373-
except Exception as ex:
374-
# Other exceptions in tiktok
375-
logger.warning(
376-
f"Failed to get tiktoken encoding for model_name {model_name}, error: {str(ex)}"
377-
)
378-
return None
379-
380-
tiktoken_encodings[model_name] = encoding
381-
else:
382-
encoding = tiktoken_encodings.get(model_name)
383-
384-
token_count = len(encoding.encode(string))
385-
return token_count
386-
387-
388355
def _token_type(token_type: str):
389356
if token_type == "prompt_tokens":
390357
return "input"

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/chat_wrappers.py

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
_set_span_attribute,
1818
_set_span_stream_usage,
1919
_token_type,
20-
get_token_count_from_string,
2120
is_streaming_response,
2221
metric_shared_attributes,
2322
model_as_dict,
2423
propagate_trace_context,
2524
set_tools_attributes,
26-
should_record_stream_token_usage,
2725
)
2826
from opentelemetry.instrumentation.openai.shared.config import Config
2927
from opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
@@ -529,48 +527,17 @@ def _set_completions(span, choices):
529527
def _set_streaming_token_metrics(
530528
request_kwargs, complete_response, span, token_counter, shared_attributes
531529
):
532-
if not should_record_stream_token_usage():
533-
return
534-
535530
prompt_usage = -1
536531
completion_usage = -1
537532

538-
# First, try to get usage from API response
533+
# Use token usage from API response only
539534
if complete_response.get("usage"):
540535
usage = complete_response["usage"]
541536
if usage.get("prompt_tokens"):
542537
prompt_usage = usage["prompt_tokens"]
543538
if usage.get("completion_tokens"):
544539
completion_usage = usage["completion_tokens"]
545540

546-
# If API response doesn't have usage, fallback to tiktoken calculation
547-
if prompt_usage == -1 or completion_usage == -1:
548-
model_name = (
549-
complete_response.get("model") or request_kwargs.get(
550-
"model") or "gpt-4"
551-
)
552-
553-
# Calculate prompt tokens if not available from API
554-
if prompt_usage == -1 and request_kwargs and request_kwargs.get("messages"):
555-
prompt_content = ""
556-
for msg in request_kwargs.get("messages"):
557-
if msg.get("content"):
558-
prompt_content += msg.get("content")
559-
if model_name and should_record_stream_token_usage():
560-
prompt_usage = get_token_count_from_string(
561-
prompt_content, model_name)
562-
563-
# Calculate completion tokens if not available from API
564-
if completion_usage == -1 and complete_response.get("choices"):
565-
completion_content = ""
566-
for choice in complete_response.get("choices"):
567-
if choice.get("message") and choice.get("message").get("content"):
568-
completion_content += choice["message"]["content"]
569-
if model_name and should_record_stream_token_usage():
570-
completion_usage = get_token_count_from_string(
571-
completion_content, model_name
572-
)
573-
574541
# span record
575542
_set_span_stream_usage(span, prompt_usage, completion_usage)
576543

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/completion_wrappers.py

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
_set_response_attributes,
99
_set_span_attribute,
1010
_set_span_stream_usage,
11-
get_token_count_from_string,
1211
is_streaming_response,
1312
model_as_dict,
1413
propagate_trace_context,
15-
should_record_stream_token_usage,
1614
)
1715
from opentelemetry.instrumentation.openai.shared.config import Config
1816
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
@@ -231,35 +229,19 @@ def _emit_streaming_response_events(complete_response):
231229

232230
@dont_throw
233231
def _set_token_usage(span, request_kwargs, complete_response):
234-
# use tiktoken calculate token usage
235-
if should_record_stream_token_usage():
236-
prompt_usage = -1
237-
completion_usage = -1
232+
prompt_usage = -1
233+
completion_usage = -1
238234

239-
# prompt_usage
240-
if request_kwargs and request_kwargs.get("prompt"):
241-
prompt_content = request_kwargs.get("prompt")
242-
model_name = complete_response.get("model") or None
235+
# Use token usage from API response only
236+
if complete_response.get("usage"):
237+
usage = complete_response["usage"]
238+
if usage.get("prompt_tokens"):
239+
prompt_usage = usage["prompt_tokens"]
240+
if usage.get("completion_tokens"):
241+
completion_usage = usage["completion_tokens"]
243242

244-
if model_name:
245-
prompt_usage = get_token_count_from_string(prompt_content, model_name)
246-
247-
# completion_usage
248-
if complete_response.get("choices"):
249-
completion_content = ""
250-
model_name = complete_response.get("model") or None
251-
252-
for choice in complete_response.get("choices"):
253-
if choice.get("text"):
254-
completion_content += choice.get("text")
255-
256-
if model_name:
257-
completion_usage = get_token_count_from_string(
258-
completion_content, model_name
259-
)
260-
261-
# span record
262-
_set_span_stream_usage(span, prompt_usage, completion_usage)
243+
# span record
244+
_set_span_stream_usage(span, prompt_usage, completion_usage)
263245

264246

265247
@dont_throw
@@ -269,6 +251,11 @@ def _accumulate_streaming_response(complete_response, item):
269251

270252
complete_response["model"] = item.get("model")
271253
complete_response["id"] = item.get("id")
254+
255+
# capture usage information from the stream chunks
256+
if item.get("usage"):
257+
complete_response["usage"] = item.get("usage")
258+
272259
for choice in item.get("choices"):
273260
index = choice.get("index")
274261
if len(complete_response.get("choices")) <= index:

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/config.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55

66
class Config:
7-
enrich_token_usage = False
87
enrich_assistant = False
98
exception_logger = None
109
get_common_metrics_attributes: Callable[[], dict] = lambda: {}

packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ def is_metrics_enabled() -> bool:
3131
return (os.getenv("TRACELOOP_METRICS_ENABLED") or "true").lower() == "true"
3232

3333

34-
def should_record_stream_token_usage():
35-
return Config.enrich_token_usage
36-
37-
3834
def _with_image_gen_metric_wrapper(func):
3935
def _with_metric(duration_histogram, exception_counter):
4036
def wrapper(wrapped, instance, args, kwargs):

0 commit comments

Comments
 (0)