Fix spend logs storing router model instead of actual selected model

get_logging_payload re-derived the model from request kwargs via
reconstruct_model_name, discarding standard_logging_payload["model"]
which already carries the Azure Model Router override (the model Azure
actually selected). Prefer the standard logging payload model when set
and fall back to kwargs reconstruction otherwise; for non-router calls
the two are built from the same inputs so behavior is unchanged.

Fixes #27942.
This commit is contained in:
Filippo Mattia Menghi 2026-06-10 10:23:34 +02:00
parent e15b37a18e
commit 72244a2bd8
2 changed files with 56 additions and 1 deletions

View file

@ -411,7 +411,13 @@ def get_logging_payload( # noqa: PLR0915
agent_id: Optional[str] = kwargs.get("agent_id") or metadata.get("agent_id")
custom_llm_provider = kwargs.get("custom_llm_provider")
raw_model = cast(str, kwargs.get("model") or "")
model_name = reconstruct_model_name(raw_model, custom_llm_provider, metadata or {})
# prefer the standard logging payload's model; it carries response-level
# overrides (e.g. the model Azure Model Router actually selected)
model_name = (
standard_logging_payload.get("model")
if standard_logging_payload is not None
else None
) or reconstruct_model_name(raw_model, custom_llm_provider, metadata or {})
try:
payload: SpendLogsPayload = SpendLogsPayload(

View file

@ -2073,3 +2073,52 @@ def test_sanitize_error_information_redacts_pydantic_assignment_form(
assert sanitized is not None
assert "leaked-via-pydantic-msg" not in sanitized["error_message"]
assert REDACTED_BY_LITELM_STRING in sanitized["error_message"]
def _model_router_spend_log_kwargs(slp_model) -> dict:
standard_logging_payload = cast(
StandardLoggingPayload,
{
"model": slp_model,
"metadata": {},
"model_map_information": StandardLoggingModelInformation(
model_map_key="azure_ai/model_router", model_map_value=None
),
},
)
return {
"model": "azure_ai/model_router/model-router",
"litellm_params": {"metadata": {"user_api_key": "sk-test-key"}},
"standard_logging_object": standard_logging_payload,
}
@patch("litellm.proxy.proxy_server.master_key", None)
@patch("litellm.proxy.proxy_server.general_settings", {})
def test_get_logging_payload_uses_standard_logging_payload_model():
"""
Azure Model Router regression: the standard logging payload model (the
model Azure actually selected) must win over the router model
reconstructed from request kwargs.
"""
payload = get_logging_payload(
kwargs=_model_router_spend_log_kwargs(slp_model="azure_ai/gpt-5-mini"),
response_obj={},
start_time=datetime.datetime.now(timezone.utc),
end_time=datetime.datetime.now(timezone.utc),
)
assert payload["model"] == "azure_ai/gpt-5-mini"
@patch("litellm.proxy.proxy_server.master_key", None)
@patch("litellm.proxy.proxy_server.general_settings", {})
def test_get_logging_payload_falls_back_to_kwargs_model_when_slp_model_missing():
payload = get_logging_payload(
kwargs=_model_router_spend_log_kwargs(slp_model=None),
response_obj={},
start_time=datetime.datetime.now(timezone.utc),
end_time=datetime.datetime.now(timezone.utc),
)
assert payload["model"] == "azure_ai/model_router/model-router"