diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index 970cd466337..515491f938f 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -674,8 +674,13 @@ class HttpPassThroughEndpointHelpers(BasePassthroughUtils): ) litellm_keys_in_body: Final = MappingProxyType( - {k: _parsed_body.pop(k) for k in types_utils.all_litellm_params if k in _parsed_body} + {k: _parsed_body[k] for k in types_utils.all_litellm_params if k in _parsed_body} ) + if request_dispatched_to_pass_through_endpoint(request): + _parsed_body.pop("litellm_logging_obj", None) + else: + for key in litellm_keys_in_body: + _parsed_body.pop(key) kept_out_of_litellm_params: Final = ( _KEPT_OUT_OF_LITELLM_PARAMS if _key_or_team_allows_client_pricing_override(user_api_key_dict) diff --git a/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py b/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py index 4fe358c38a3..3e103a20f55 100644 --- a/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py +++ b/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py @@ -74,6 +74,7 @@ from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import CallTypes from litellm.types.passthrough_endpoints.pass_through_endpoints import ( LITELLM_PASS_THROUGH_DEPLOYMENT_MODEL_INFO_STATE_KEY, + LITELLM_PASS_THROUGH_ENDPOINT_MARKER, LITELLM_PASS_THROUGH_RAW_BODY_STATE_KEY, EndpointType, PassthroughStandardLoggingPayload, @@ -6638,6 +6639,7 @@ async def _relay_through_pass_through( target: str, guardrails_config: dict[str, object] | None, custom_body: dict[str, object] | None = None, + custom_endpoint: bool = False, ) -> Mapping[str, object]: async def receive() -> Mapping[str, object]: return {"type": "http.request", "body": json.dumps(dict(client_body)).encode()} @@ -6649,6 +6651,7 @@ async def _relay_through_pass_through( "path": "/my-claude-route", "query_string": b"", "headers": [(b"content-type", b"application/json")], + "endpoint": SimpleNamespace(**{LITELLM_PASS_THROUGH_ENDPOINT_MARKER: True}) if custom_endpoint else None, }, receive, ) @@ -6676,6 +6679,17 @@ async def _relay_through_pass_through( return MappingProxyType(json.loads(relayed)) +@pytest.mark.parametrize("request_id", [1, "request-1", None]) +async def test_custom_pass_through_preserves_json_rpc_request_id(request_id: int | str | None) -> None: + body: Final = {"jsonrpc": "2.0", "id": request_id, "method": "skills/list", "params": {}, "canary": "kept"} + + upstream_body: Final = await _relay_through_pass_through( + body, "http://upstream.test/mcp", guardrails_config=None, custom_endpoint=True + ) + + assert upstream_body == body + + _MESSAGES_BODY: Final = MappingProxyType( {"model": "claude-sonnet-4-5", "max_tokens": 16, "messages": [{"role": "user", "content": "hi"}]} )