From de0db82320d3ec274b8b60f1d32a981891d968c4 Mon Sep 17 00:00:00 2001 From: RoyVivat Date: Wed, 18 Mar 2026 16:23:27 -0700 Subject: [PATCH] Updated get_logging_id in utils.py to strip s3://bucket from id. Added tests --- litellm/utils.py | 10 +++-- tests/test_litellm/integrations/test_s3_v2.py | 37 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index 81d749ab821..ef67ea74894 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -7401,9 +7401,13 @@ def print_args_passed_to_litellm(original_function, args, kwargs): def get_logging_id(start_time, response_obj): try: - response_id = ( - "time-" + start_time.strftime("%H-%M-%S-%f") + "_" + response_obj.get("id") - ) + raw_id = response_obj.get("id") or "" + # Batch API IDs may be S3 URIs (e.g. "s3://bucket/key"). Strip the scheme and bucket, keeping only the object key to use as the log ID. + if "://" in raw_id: + safe_id = raw_id.split("://", 1)[1].split("/", 1)[-1].replace("/", "-") + else: + safe_id = raw_id + response_id = "time-" + start_time.strftime("%H-%M-%S-%f") + "_" + safe_id return response_id except Exception: return None diff --git a/tests/test_litellm/integrations/test_s3_v2.py b/tests/test_litellm/integrations/test_s3_v2.py index b53c05fa241..7dc25043fd5 100644 --- a/tests/test_litellm/integrations/test_s3_v2.py +++ b/tests/test_litellm/integrations/test_s3_v2.py @@ -6,6 +6,7 @@ import pytest from litellm.integrations.s3_v2 import S3Logger from litellm.types.utils import StandardLoggingPayload +from litellm.utils import get_logging_id class TestS3V2UnitTests: @@ -661,6 +662,42 @@ async def test_strip_base64_recursive_redaction(): +# -------------------------------------------------------------- +# Tests for get_logging_id S3 URI sanitization (issue #23904) +# -------------------------------------------------------------- +def test_get_logging_id_strips_s3_uri_scheme_and_bucket(): + """Bedrock batch file IDs are S3 URIs — they must not appear verbatim in log paths.""" + start_time = datetime(2026, 3, 9, 17, 40, 11, 901585) + response_obj = { + "id": "s3://bucket-int/litellm-bedrock-files-us.anthropic.claude-sonnet-4-5-20250929-v1-0-29ea93-452e-8a2f.jsonl" + } + result = get_logging_id(start_time, response_obj) + assert "://" not in result, "S3 URI scheme must be stripped from log ID" + assert result.startswith("time-17-40-11-901585_") + # Bucket name should be gone; object key should be present + assert "bucket-int" not in result + assert "litellm-bedrock-files-us.anthropic.claude-sonnet-4-5-20250929-v1-0-29ea93-452e-8a2f.jsonl" in result + + +def test_get_logging_id_normal_id_unchanged(): + """Normal completion IDs must pass through without modification.""" + start_time = datetime(2026, 3, 9, 17, 40, 11, 901585) + response_obj = {"id": "chatcmpl-abc123"} + result = get_logging_id(start_time, response_obj) + assert result == "time-17-40-11-901585_chatcmpl-abc123" + + +def test_get_logging_id_s3_uri_safe_for_url_path(): + """The sanitized log ID must be embeddable in an S3 object key URL without creating a malformed path.""" + start_time = datetime(2026, 3, 9, 17, 40, 11, 901585) + response_obj = {"id": "s3://my-bucket/subdir/file.jsonl"} + result = get_logging_id(start_time, response_obj) + # Simulate how s3_v2 embeds this in a URL + url = f"https://my-log-bucket.s3.us-east-1.amazonaws.com/LiteLLMAPPLogs/2026-03-09/{result}.json" + assert "s3://" not in url + assert url.count("://") == 1, "URL must contain exactly one scheme separator" + + # -------------------------------------------------------------- # Shared fixture that silences asyncio.create_task during tests # --------------------------------------------------------------