@@ -1014,91 +1014,95 @@ async def test_add_litellm_metadata_from_request_headers():
10141014 # Set up test logger
10151015 litellm ._turn_on_debug ()
10161016 test_logger = TestCustomLogger ()
1017+ original_callbacks = litellm .callbacks
10171018 litellm .callbacks = [test_logger ]
10181019
1019- # Prepare test data (ensure no streaming, add mock_response and api_key to route to litellm.acompletion)
1020- headers = {"x-litellm-spend-logs-metadata" : '{"user_id": "12345", "project_id": "proj_abc", "request_type": "chat_completion", "timestamp": "2025-09-02T10:30:00Z"}' }
1021- data = {"model" : "gpt-4" , "messages" : [{"role" : "user" , "content" : "Hello" }], "stream" : False , "mock_response" : "Hi" , "api_key" : "fake-key" }
1022-
1023- # Create mock request with headers
1024- mock_request = MagicMock (spec = Request )
1025- mock_request .headers = headers
1026- mock_request .url .path = "/chat/completions"
1027-
1028- # Create mock response
1029- mock_fastapi_response = MagicMock (spec = Response )
1030-
1031- # Create mock user API key dict
1032- mock_user_api_key_dict = UserAPIKeyAuth (
1033- api_key = "test-key" ,
1034- user_id = "test-user" ,
1035- org_id = "test-org"
1036- )
1037-
1038- # Create mock proxy logging object
1039- mock_proxy_logging_obj = MagicMock (spec = ProxyLogging )
1040-
1041- # Create async functions for the hooks
1042- async def mock_during_call_hook (* args , ** kwargs ):
1043- return None
1044-
1045- async def mock_pre_call_hook (* args , ** kwargs ):
1046- return data
1047-
1048- async def mock_post_call_success_hook (* args , ** kwargs ):
1049- # Return the response unchanged
1050- return kwargs .get ('response' , args [2 ] if len (args ) > 2 else None )
1051-
1052- mock_proxy_logging_obj .during_call_hook = mock_during_call_hook
1053- mock_proxy_logging_obj .pre_call_hook = mock_pre_call_hook
1054- mock_proxy_logging_obj .post_call_success_hook = mock_post_call_success_hook
1055-
1056- # Create mock proxy config
1057- mock_proxy_config = MagicMock ()
1058-
1059- # Create mock general settings
1060- general_settings = {}
1061-
1062- # Create mock select_data_generator with correct signature
1063- def mock_select_data_generator (response = None , user_api_key_dict = None , request_data = None ):
1064- async def mock_generator ():
1065- yield "data: " + json .dumps ({"choices" : [{"delta" : {"content" : "Hello" }}]}) + "\n \n "
1066- yield "data: [DONE]\n \n "
1067- return mock_generator ()
1068-
1069- # Create the processor
1070- processor = ProxyBaseLLMRequestProcessing (data = data )
1071-
1072- # Call base_process_llm_request (it will use the mock_response="Hi" parameter)
1073- result = await processor .base_process_llm_request (
1074- request = mock_request ,
1075- fastapi_response = mock_fastapi_response ,
1076- user_api_key_dict = mock_user_api_key_dict ,
1077- route_type = "acompletion" ,
1078- proxy_logging_obj = mock_proxy_logging_obj ,
1079- general_settings = general_settings ,
1080- proxy_config = mock_proxy_config ,
1081- select_data_generator = mock_select_data_generator ,
1082- llm_router = None ,
1083- model = "gpt-4" ,
1084- is_streaming_request = False
1085- )
1086-
1087- # Sleep for 3 seconds to allow logging to complete
1088- await asyncio .sleep (3 )
1089-
1090- # Check if standard_logging_object was set
1091- assert test_logger .standard_logging_object is not None , "standard_logging_object should be populated after LLM request"
1092-
1093- # Verify the logging object contains expected metadata
1094- standard_logging_obj = test_logger .standard_logging_object
1020+ try :
1021+ # Prepare test data (ensure no streaming, add mock_response and api_key to route to litellm.acompletion)
1022+ headers = {"x-litellm-spend-logs-metadata" : '{"user_id": "12345", "project_id": "proj_abc", "request_type": "chat_completion", "timestamp": "2025-09-02T10:30:00Z"}' }
1023+ data = {"model" : "gpt-4" , "messages" : [{"role" : "user" , "content" : "Hello" }], "stream" : False , "mock_response" : "Hi" , "api_key" : "fake-key" }
1024+
1025+ # Create mock request with headers
1026+ mock_request = MagicMock (spec = Request )
1027+ mock_request .headers = headers
1028+ mock_request .url .path = "/chat/completions"
1029+
1030+ # Create mock response
1031+ mock_fastapi_response = MagicMock (spec = Response )
1032+
1033+ # Create mock user API key dict
1034+ mock_user_api_key_dict = UserAPIKeyAuth (
1035+ api_key = "test-key" ,
1036+ user_id = "test-user" ,
1037+ org_id = "test-org"
1038+ )
10951039
1096- print (f"Standard logging object captured: { json .dumps (standard_logging_obj , indent = 4 , default = str )} " )
1040+ # Create mock proxy logging object
1041+ mock_proxy_logging_obj = MagicMock (spec = ProxyLogging )
1042+
1043+ # Create async functions for the hooks
1044+ async def mock_during_call_hook (* args , ** kwargs ):
1045+ return None
1046+
1047+ async def mock_pre_call_hook (* args , ** kwargs ):
1048+ return data
1049+
1050+ async def mock_post_call_success_hook (* args , ** kwargs ):
1051+ # Return the response unchanged
1052+ return kwargs .get ('response' , args [2 ] if len (args ) > 2 else None )
1053+
1054+ mock_proxy_logging_obj .during_call_hook = mock_during_call_hook
1055+ mock_proxy_logging_obj .pre_call_hook = mock_pre_call_hook
1056+ mock_proxy_logging_obj .post_call_success_hook = mock_post_call_success_hook
1057+
1058+ # Create mock proxy config
1059+ mock_proxy_config = MagicMock ()
1060+
1061+ # Create mock general settings
1062+ general_settings = {}
1063+
1064+ # Create mock select_data_generator with correct signature
1065+ def mock_select_data_generator (response = None , user_api_key_dict = None , request_data = None ):
1066+ async def mock_generator ():
1067+ yield "data: " + json .dumps ({"choices" : [{"delta" : {"content" : "Hello" }}]}) + "\n \n "
1068+ yield "data: [DONE]\n \n "
1069+ return mock_generator ()
1070+
1071+ # Create the processor
1072+ processor = ProxyBaseLLMRequestProcessing (data = data )
1073+
1074+ # Call base_process_llm_request (it will use the mock_response="Hi" parameter)
1075+ result = await processor .base_process_llm_request (
1076+ request = mock_request ,
1077+ fastapi_response = mock_fastapi_response ,
1078+ user_api_key_dict = mock_user_api_key_dict ,
1079+ route_type = "acompletion" ,
1080+ proxy_logging_obj = mock_proxy_logging_obj ,
1081+ general_settings = general_settings ,
1082+ proxy_config = mock_proxy_config ,
1083+ select_data_generator = mock_select_data_generator ,
1084+ llm_router = None ,
1085+ model = "gpt-4" ,
1086+ is_streaming_request = False
1087+ )
1088+
1089+ # Sleep for 3 seconds to allow logging to complete
1090+ await asyncio .sleep (3 )
1091+
1092+ # Check if standard_logging_object was set
1093+ assert test_logger .standard_logging_object is not None , "standard_logging_object should be populated after LLM request"
1094+
1095+ # Verify the logging object contains expected metadata
1096+ standard_logging_obj = test_logger .standard_logging_object
1097+
1098+ print (f"Standard logging object captured: { json .dumps (standard_logging_obj , indent = 4 , default = str )} " )
1099+
1100+ SPEND_LOGS_METADATA = standard_logging_obj ["metadata" ]["spend_logs_metadata" ]
1101+ assert SPEND_LOGS_METADATA == dict (json .loads (headers ["x-litellm-spend-logs-metadata" ])), "spend_logs_metadata should be the same as the headers"
1102+ finally :
1103+ litellm .callbacks = original_callbacks
10971104
1098- SPEND_LOGS_METADATA = standard_logging_obj ["metadata" ]["spend_logs_metadata" ]
1099- assert SPEND_LOGS_METADATA == dict (json .loads (headers ["x-litellm-spend-logs-metadata" ])), "spend_logs_metadata should be the same as the headers"
11001105
1101-
11021106
11031107def test_get_internal_user_header_from_mapping_returns_expected_header ():
11041108 mappings = [
0 commit comments