mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-30 01:52:18 +00:00
test(otel): regression test for None metadata values on spans
Covers issue #29583: a None-valued metadata field (e.g. user_api_key_budget_reset_at with no budget configured) should be omitted from the span, not sent as an empty string. Also asserts a real timestamp value for the same field is still set correctly, so the fix doesn't just suppress the field entirely. Verified against a control: this test fails on the pre-fix code and passes with it.
This commit is contained in:
parent
bf8c35e2d4
commit
d010ed4267
1 changed files with 80 additions and 0 deletions
|
|
@ -391,6 +391,86 @@ class TestOpenTelemetryCostBreakdown(unittest.TestCase):
|
|||
assert ("gen_ai.cost.original_cost", 0.004) not in call_args_list
|
||||
|
||||
|
||||
class TestOpenTelemetryMetadataNoneValues(unittest.TestCase):
|
||||
"""
|
||||
Regression test for https://github.com/BerriAI/litellm/issues/29583
|
||||
|
||||
None-valued metadata fields (e.g. user_api_key_budget_reset_at for a key
|
||||
with no budget configured) were being cast to "" and set on the span like
|
||||
any other value. A field that is sometimes a real value and sometimes ""
|
||||
sends two different shapes for the same attribute -- backends that infer
|
||||
a field's type from the first value they see (e.g. OpenSearch's dynamic
|
||||
mapping) lock onto whichever shape arrives first, so the other shape then
|
||||
fails. None-valued metadata should be omitted from the span instead.
|
||||
"""
|
||||
|
||||
def test_none_metadata_value_is_not_set_on_span(self):
|
||||
otel = OpenTelemetry()
|
||||
mock_span = MagicMock()
|
||||
|
||||
kwargs = {
|
||||
"model": "gpt-4",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"optional_params": {},
|
||||
"litellm_params": {"custom_llm_provider": "openai"},
|
||||
"standard_logging_object": {
|
||||
"id": "test-id",
|
||||
"call_type": "completion",
|
||||
"metadata": {
|
||||
"user_api_key_budget_reset_at": None,
|
||||
"user_api_key_team_id": "team-123",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
call_args_list = [call[0] for call in mock_span.set_attribute.call_args_list]
|
||||
set_keys = [args[0] for args in call_args_list]
|
||||
|
||||
assert "metadata.user_api_key_budget_reset_at" not in set_keys
|
||||
assert ("metadata.user_api_key_budget_reset_at", "") not in call_args_list
|
||||
mock_span.set_attribute.assert_any_call("metadata.user_api_key_team_id", "team-123")
|
||||
|
||||
def test_real_timestamp_metadata_value_is_still_set_on_span(self):
|
||||
otel = OpenTelemetry()
|
||||
mock_span = MagicMock()
|
||||
|
||||
kwargs = {
|
||||
"model": "gpt-4",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"optional_params": {},
|
||||
"litellm_params": {"custom_llm_provider": "openai"},
|
||||
"standard_logging_object": {
|
||||
"id": "test-id",
|
||||
"call_type": "completion",
|
||||
"metadata": {
|
||||
"user_api_key_budget_reset_at": "2026-09-15T00:00:00+00:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
mock_span.set_attribute.assert_any_call(
|
||||
"metadata.user_api_key_budget_reset_at", "2026-09-15T00:00:00+00:00"
|
||||
)
|
||||
|
||||
|
||||
class TestOpenTelemetryProviderInitialization(unittest.TestCase):
|
||||
"""Test suite for verifying provider initialization respects existing providers"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue