This commit is contained in:
Hari 2026-10-27 02:25:28 +00:00 • committed by GitHub
commit e89646c268
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 38 additions and 17 deletions

View file

@ -262,24 +262,9 @@ class PostHogLogger(CustomBatchLogger):
litellm_internal_fields: Final = {
"endpoint",
"caching_groups",
"user_api_key_hash",
"user_api_key_alias",
"user_api_key_team_id",
"user_api_key_user_id",
"user_api_key_org_id",
"user_api_key_team_alias",
"user_api_key_end_user_id",
"user_api_key_user_email",
"user_api_key",
"user_api_end_user_max_budget",
"litellm_api_version",
"global_max_parallel_requests",
"user_api_key_team_max_budget",
"user_api_key_team_spend",
"user_api_key_spend",
"user_api_key_max_budget",
"user_api_key_model_max_budget",
"user_api_key_metadata",
"headers",
"litellm_parent_otel_span",
"requester_ip_address",
@ -295,8 +280,13 @@ class PostHogLogger(CustomBatchLogger):
}
for key, value in metadata.items():
if key not in litellm_internal_fields:
properties[key] = value
# The proxy writes the calling key's record under user_api_key* names
# (the hash, budgets, spend, the whole UserAPIKeyAuth object), and the
# set of names grows with the proxy, so the rule is the prefix, as in
# redact_user_api_key_info, not a list.
if key.startswith("user_api_key") or key in litellm_internal_fields:
continue
properties[key] = value
def _get_distinct_id(self, standard_logging_object: StandardLoggingPayload, kwargs: PostHogLogKwargs) -> str:
metadata: Final = self._extract_metadata(kwargs)

View file

@ -243,6 +243,37 @@ async def test_custom_metadata_filters_internal_fields():
assert "model_info" not in event_payload["properties"]
@pytest.mark.asyncio
async def test_custom_metadata_skips_every_key_record_field():
"""The proxy writes the calling key's record into the request metadata under
user_api_key* names, including ones no fixed list names: the whole UserAPIKeyAuth
object, its metadata, the budget reservation and the request route. None of them
is a custom property, and the key's hash and budgets must not reach PostHog."""
posthog_logger = PostHogLogger()
standard_payload = create_standard_logging_payload()
kwargs = {
"standard_logging_object": standard_payload,
"litellm_params": {
"metadata": {
"custom_field": "should_appear",
"user_api_key_auth": {"token": "hash123", "max_budget": 10.0, "spend": 2.5},
"user_api_key_auth_metadata": {"tags": ["prod"]},
"user_api_key_budget_reservation": {"amount": 1.0},
"user_api_key_request_route": "/chat/completions",
"user_api_key_user_spend": 2.5,
"user_api_key_team_metadata": {"team": "legal"},
}
},
}
event_payload = posthog_logger.create_posthog_event_payload(kwargs)
assert event_payload["properties"]["custom_field"] == "should_appear"
leaked = [key for key in event_payload["properties"] if key.startswith("user_api_key")]
assert leaked == []
@pytest.mark.asyncio
async def test_custom_metadata_with_no_metadata():
"""Test that logger handles cases with no metadata gracefully"""