This commit is contained in:
Rachit 2026-11-07 00:56:41 +00:00 • committed by GitHub
commit 12e4c7de55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 95 additions and 29 deletions

View file

@ -542,12 +542,14 @@ def _set_request_attributes(
litellm_params.get("custom_llm_provider", "Unknown"),
)
if optional_params.get("max_tokens"):
safe_set_attribute(span, "llm.request.max_tokens", optional_params.get("max_tokens"))
if optional_params.get("temperature"):
safe_set_attribute(span, "llm.request.temperature", optional_params.get("temperature"))
if optional_params.get("top_p"):
safe_set_attribute(span, "llm.request.top_p", optional_params.get("top_p"))
for source_key, request_attribute in (
("max_tokens", "llm.request.max_tokens"),
("temperature", "llm.request.temperature"),
("top_p", "llm.request.top_p"),
):
request_value = optional_params.get(source_key)
if request_value is not None:
safe_set_attribute(span, request_attribute, request_value)
safe_set_attribute(span, "llm.is_streaming", str(optional_params.get("stream", False)))

View file

@ -2552,29 +2552,14 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger):
value=provider_name,
)
# The maximum number of tokens the LLM generates for a request.
if optional_params.get("max_tokens"):
self.safe_set_attribute(
span=span,
key=SpanAttributes.LLM_REQUEST_MAX_TOKENS.value,
value=optional_params.get("max_tokens"),
)
# The temperature setting for the LLM request.
if optional_params.get("temperature"):
self.safe_set_attribute(
span=span,
key=SpanAttributes.LLM_REQUEST_TEMPERATURE.value,
value=optional_params.get("temperature"),
)
# The top_p sampling setting for the LLM request.
if optional_params.get("top_p"):
self.safe_set_attribute(
span=span,
key=SpanAttributes.LLM_REQUEST_TOP_P.value,
value=optional_params.get("top_p"),
)
for source_key, request_attribute in (
("max_tokens", SpanAttributes.LLM_REQUEST_MAX_TOKENS.value),
("temperature", SpanAttributes.LLM_REQUEST_TEMPERATURE.value),
("top_p", SpanAttributes.LLM_REQUEST_TOP_P.value),
):
request_value = optional_params.get(source_key)
if request_value is not None:
self.safe_set_attribute(span=span, key=request_attribute, value=request_value)
if self._gen_ai_semconv_latest_experimental:
# Semconv emits gen_ai.request.stream (only when streaming) via

View file

@ -185,6 +185,44 @@ def test_arize_set_attributes():
span.set_attribute.assert_any_call(SpanAttributes.LLM_TOKEN_COUNT_PROMPT, 40)
def test_arize_records_zero_valued_sampling_params():
"""A sampling param the caller set to 0 still has to reach the span.
temperature=0 is what you send for deterministic output, so dropping it
hides the single most common setting from the trace.
"""
from unittest.mock import MagicMock
from litellm.types.utils import ModelResponse
span = MagicMock()
kwargs = {
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Basic Request Content"}],
"standard_logging_object": {
"model_parameters": {},
"metadata": {},
"call_type": "completion",
},
"optional_params": {"max_tokens": 0, "temperature": 0, "top_p": 0},
"litellm_params": {"custom_llm_provider": "openai"},
}
response_obj = ModelResponse(
usage={"total_tokens": 100, "completion_tokens": 60, "prompt_tokens": 40},
choices=[Choices(message={"role": "assistant", "content": "Basic Response Content"})],
model="gpt-4o",
id="chatcmpl-ID",
)
ArizeLogger.set_arize_attributes(span, kwargs, response_obj)
span.set_attribute.assert_any_call("llm.request.temperature", 0)
span.set_attribute.assert_any_call("llm.request.top_p", 0)
span.set_attribute.assert_any_call("llm.request.max_tokens", 0)
def test_arize_set_attributes_responses_api():
"""
Test setting attributes for Responses API with mixed output (reasoning + message).

View file

@ -690,6 +690,47 @@ class TestOpenTelemetryCostBreakdown(unittest.TestCase):
assert all(not key.startswith("gen_ai.cost.") for key, _value in call_args_list)
class TestOpenTelemetryZeroValuedRequestParams(unittest.TestCase):
def test_zero_valued_sampling_params_reach_the_span(self):
"""A sampling param the caller set to 0 still has to reach the span.
temperature=0 is what you send for deterministic output, so dropping it
hides the single most common setting from the trace. The semconv path
already records these, so the legacy path has to agree.
"""
otel = OpenTelemetry()
mock_span = MagicMock()
kwargs = {
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello"}],
"optional_params": {"max_tokens": 0, "temperature": 0, "top_p": 0},
"litellm_params": {"custom_llm_provider": "openai"},
"standard_logging_object": {
"id": "test-id",
"call_type": "completion",
"metadata": {},
},
}
response_obj = {
"id": "test-response-id",
"model": "gpt-4",
"choices": [],
"usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
}
otel.set_attributes(span=mock_span, kwargs=kwargs, response_obj=response_obj)
from litellm.proxy._types import SpanAttributes
recorded = dict(call[0] for call in mock_span.set_attribute.call_args_list if len(call[0]) == 2)
assert recorded.get(SpanAttributes.LLM_REQUEST_TEMPERATURE.value) == 0
assert recorded.get(SpanAttributes.LLM_REQUEST_TOP_P.value) == 0
assert recorded.get(SpanAttributes.LLM_REQUEST_MAX_TOKENS.value) == 0
class TestOpenTelemetryProviderInitialization(unittest.TestCase):
"""Test suite for verifying provider initialization respects existing providers"""