fix(proxy): move pricing strip below the litellm_metadata JSON-string parse
The strip ran before the proxy parses ``litellm_metadata`` from a JSON string into a dict (a path used by multipart/form-data and ``extra_body`` callers), so ``isinstance(metadata, dict)`` was False and ``model_info`` survived the strip. Move the call to the same post-parse position the ``user_api_key_*`` strip already uses for the same reason. Adds a regression test exercising the JSON-string ``litellm_metadata`` path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
25671105e2
commit
4b4b4a79b9
@ -1164,8 +1164,6 @@ async def add_litellm_data_to_request( # noqa: PLR0915
|
||||
if _allow_client_mock_response and _internal_key in _CLIENT_MOCK_CONTROL_FIELDS:
|
||||
continue
|
||||
data.pop(_internal_key, None)
|
||||
if not _key_or_team_allows_client_pricing_override(user_api_key_dict):
|
||||
_strip_client_pricing_overrides(data)
|
||||
# Strip spoofable auth metadata from user-supplied metadata dict
|
||||
_user_metadata = data.get("metadata")
|
||||
if isinstance(_user_metadata, dict):
|
||||
@ -1367,6 +1365,14 @@ async def add_litellm_data_to_request( # noqa: PLR0915
|
||||
]:
|
||||
_user_meta.pop(_k, None)
|
||||
|
||||
# Strip pricing overrides AFTER the litellm_metadata string-to-dict parse
|
||||
# above, for the same reason as the user_api_key_* strip — JSON-string
|
||||
# metadata (sent via multipart/form-data or extra_body) wouldn't be a
|
||||
# dict yet at the earlier strip point and the isinstance(dict) guard
|
||||
# would silently skip the field.
|
||||
if not _key_or_team_allows_client_pricing_override(user_api_key_dict):
|
||||
_strip_client_pricing_overrides(data)
|
||||
|
||||
# Strip caller-supplied routing/budget tags unless the admin has opted
|
||||
# this key or team in via metadata.allow_client_tags=True. Tags drive
|
||||
# tag-based routing and tag budget attribution — accepting them from
|
||||
|
||||
@ -231,6 +231,35 @@ async def test_add_litellm_data_to_request_skips_strip_with_key_opt_in():
|
||||
assert updated["metadata"]["model_info"] == {"output_cost_per_token": 0.0002}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_litellm_data_to_request_strips_json_string_litellm_metadata():
|
||||
"""``litellm_metadata`` may arrive as a JSON-encoded string (multipart/
|
||||
form-data or ``extra_body``). The strip has to run after the proxy parses
|
||||
it into a dict; otherwise the ``isinstance(dict)`` guard skips the field
|
||||
and ``model_info`` survives the strip via the string path.
|
||||
"""
|
||||
import json
|
||||
|
||||
data = {
|
||||
"model": "gpt-4",
|
||||
"messages": [{"role": "user", "content": "hi"}],
|
||||
"litellm_metadata": json.dumps({"model_info": {"input_cost_per_token": 0.0}}),
|
||||
}
|
||||
|
||||
updated = await add_litellm_data_to_request(
|
||||
data=data,
|
||||
request=_make_request_mock(),
|
||||
user_api_key_dict=_user_api_key_auth(),
|
||||
proxy_config=MagicMock(),
|
||||
general_settings={},
|
||||
version="test-version",
|
||||
)
|
||||
|
||||
parsed_metadata = updated.get("litellm_metadata")
|
||||
assert isinstance(parsed_metadata, dict)
|
||||
assert "model_info" not in parsed_metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_litellm_data_to_request_skips_strip_with_team_opt_in():
|
||||
data = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user