Skip to content
Merged
8 changes: 7 additions & 1 deletion litellm/proxy/dd_span_tagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ def tag_request(
- ``litellm.key_alias`` — human-readable alias for the API key
- ``litellm.key_hash`` — hashed API key (safe to log; never the raw secret)
- ``litellm.requested_model``— model name as sent by the client
- ``litellm.user_email`` — email of the authenticated user

Use cases:
- Trace all requests from a specific user/key: filter by ``litellm.key_alias`` or
``litellm.key_hash``.
- Trace all requests for a specific model: filter by ``litellm.requested_model``.
- Trace all requests from a specific person under JWT auth, where there is no key_alias:
filter by ``@litellm.user_email:"user@example.com"``. The value comes from the JWT claim
mapped by ``litellm_jwtauth.user_email_jwt_field``, or from the user row for virtual keys.

Note: key_alias / key_hash are not available for unauthenticated (e.g. 401) requests.
"""
Expand All @@ -51,8 +55,10 @@ def tag_request(
set_active_span_tag("litellm.key_hash", str(user_api_key_dict.token))
if requested_model:
set_active_span_tag("litellm.requested_model", str(requested_model))
if user_api_key_dict.user_email:
set_active_span_tag("litellm.user_email", str(user_api_key_dict.user_email))
except Exception:
verbose_proxy_logger.debug(
"Failed to tag active ddtrace span with key/model tags",
"Failed to tag active ddtrace span with key/model/user tags",
exc_info=True,
)
5 changes: 5 additions & 0 deletions litellm/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -11396,6 +11396,10 @@ def _set_model_group_info(self, model_group: str, user_facing_model_group_name:
reasoning_efforts_initialized = False
reasoning_efforts_unknown = False
model_list: Final = self.get_model_list_of_routed_group(model_group)
model_group_description: Final[str | None] = next(
(d for d in (m.get("model_info", {}).get("description") for m in model_list) if isinstance(d, str)),
None,
)
for model in model_list:
is_match = False
if (
Expand Down Expand Up @@ -11501,6 +11505,7 @@ def _set_model_group_info(self, model_group: str, user_facing_model_group_name:
**{
"model_group": user_facing_model_group_name,
"providers": [llm_provider],
"description": model_group_description,
**model_info,
"supports_fast_mode": True,
"supported_reasoning_efforts": None,
Expand Down
1 change: 1 addition & 0 deletions litellm/types/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ class ModelGroupInfo(LiteLLMBaseModel):
supported_reasoning_efforts: tuple[str, ...] | None = Field(default=None)
supported_openai_params: list[str] | None = Field(default=[])
configurable_clientside_auth_params: CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS = None
description: str | None = None

def __init__(self, **data) -> None:
for field_name, field_type in _resolved_annotations(self.__class__).items():
Expand Down
27 changes: 26 additions & 1 deletion tests/unit/proxy/test_common_request_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3890,12 +3890,13 @@ def test_streaming_overhead_header_in_custom_headers_from_stream_hidden_params(
class TestDDSpanTaggerTagRequest:
"""Tests for DDSpanTagger.tag_request - key/model DD span tagging."""

def _make_user_api_key_dict(self, key_alias=None, token=None):
def _make_user_api_key_dict(self, key_alias=None, token=None, user_email=None):
from litellm.proxy._types import UserAPIKeyAuth

d = UserAPIKeyAuth()
d.key_alias = key_alias
d.token = token
d.user_email = user_email
return d

def test_tags_key_alias_and_model(self):
Expand Down Expand Up @@ -3936,6 +3937,30 @@ def test_only_model_tagged_when_no_key_info(self):

mock_set_tag.assert_called_once_with("litellm.requested_model", "claude-3-5-sonnet")

def test_tags_user_email(self):
"""user_email is tagged so JWT-authenticated requests are traceable per person."""
user_key = self._make_user_api_key_dict(user_email="user@example.com")

with patch("litellm.proxy.dd_span_tagger.set_active_span_tag") as mock_set_tag:
DDSpanTagger.tag_request(
user_api_key_dict=user_key,
requested_model=None,
)

mock_set_tag.assert_called_once_with("litellm.user_email", "user@example.com")

def test_no_user_email_tag_when_absent(self):
"""No user email tag when the authenticated identity has no email."""
user_key = self._make_user_api_key_dict(key_alias="my-prod-key", user_email=None)

with patch("litellm.proxy.dd_span_tagger.set_active_span_tag") as mock_set_tag:
DDSpanTagger.tag_request(
user_api_key_dict=user_key,
requested_model="gpt-4o",
)

assert all(call.args[0] != "litellm.user_email" for call in mock_set_tag.call_args_list)


class TestHasAttributeErrorInChain:
"""Tests for _has_attribute_error_in_chain helper."""
Expand Down
25 changes: 25 additions & 0 deletions tests/unit/test_router/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2318,6 +2318,31 @@ def test_model_group_info_cost_none_for_unpriced_deployment_but_zero_when_declar
assert priced.output_cost_per_token is not None and priced.output_cost_per_token > 0


def test_model_group_info_description_from_model_info():
"""
model_info.description set in the config should surface on ModelGroupInfo,
including when only a later deployment in the group carries it.
"""
router = litellm.Router(
model_list=[
{
"model_name": "gpt-4",
"litellm_params": {"model": "gpt-4", "api_key": "fake"},
"model_info": {},
},
{
"model_name": "gpt-4",
"litellm_params": {"model": "gpt-4", "api_key": "fake2"},
"model_info": {"description": "State-of-the-art language model."},
},
]
)

result = router.get_model_group_info("gpt-4")
assert result is not None
assert result.description == "State-of-the-art language model."


def _alias_cost_router() -> Router:
return Router(
model_list=[
Expand Down
6 changes: 6 additions & 0 deletions ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
premiumUser,
userRole,
canOpenPlayground,
}) => {

Check warning on line 79 in ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx

View workflow job for this annotation

GitHub Actions / ui

Arrow function has a complexity of 92. Maximum allowed is 20
const syntaxTheme = useSyntaxTheme(prism);
// Admin Viewer follows the read-parity rule: see the AI Hub catalog, but
// cannot toggle public visibility (write).
Expand Down Expand Up @@ -192,7 +192,7 @@
setAgentLoading(true);
const response = await getAgentsList(accessToken);
let agents = response.agents;
let agent_card_list = agents.map((agent: any) => ({

Check warning on line 195 in ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
agent_id: agent.agent_id,
...agent.agent_card_params,
is_public: agent.litellm_params.is_public,
Expand Down Expand Up @@ -355,7 +355,7 @@
try {
const response = await getAgentsList(accessToken);
let agents = response.agents;
let agent_card_list = agents.map((agent: any) => ({

Check warning on line 358 in ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
agent_id: agent.agent_id,
...agent.agent_card_params,
is_public: agent.is_public,
Expand Down Expand Up @@ -684,6 +684,12 @@
))}
</div>
</div>
{selectedModel.description && (
<div className="col-span-2">
<p className="font-medium">Description:</p>
<p className="mt-1">{selectedModel.description}</p>
</div>
)}
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
supports_function_calling: boolean;
supported_openai_params?: string[];
is_public_model_group: boolean;
description?: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Description Column Is Missing

Both model tables use explicit column lists, but neither list includes description. The new field appears only after opening a model's details, so users cannot see the promised Description column in the Model Hub table.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description renders in each rows expanded details panel, matching the merged upstream implementation. The tables explicit column list was intentionally left unchanged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re right — the stated user flow promises the description in the expanded model details, and this change implements that in both model hub tables. The explicit column lists are intentionally unchanged, so my previous finding does not apply; no change is needed.

[key: string]: any;

Check warning on line 35 in ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
}

const formatCapabilityName = (key: string) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
health_status?: string;
health_response_time?: number;
health_checked_at?: string;
description?: string;
[key: string]: any;

Check warning on line 29 in ui/litellm-dashboard/src/components/PublicModelHubTableColumns.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
}

export interface AgentCard {
Expand Down Expand Up @@ -53,7 +54,7 @@
url: string;
};
documentationUrl?: string;
[key: string]: any;

Check warning on line 57 in ui/litellm-dashboard/src/components/PublicModelHubTableColumns.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
}

export interface MCPServerData {
Expand All @@ -67,9 +68,9 @@
mcp_info: {
server_name: string;
description?: string;
mcp_server_cost_info?: any;

Check warning on line 71 in ui/litellm-dashboard/src/components/PublicModelHubTableColumns.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
};
[key: string]: any;

Check warning on line 73 in ui/litellm-dashboard/src/components/PublicModelHubTableColumns.tsx

View workflow job for this annotation

GitHub Actions / ui

Unexpected any. Specify a different type
}

const formatCapabilityName = (key: string) =>
Expand Down Expand Up @@ -118,7 +119,7 @@
return (
<span key={provider} className="flex items-center gap-1 rounded-md bg-muted px-2 py-1 text-xs">
{logo && (
<img

Check warning on line 122 in ui/litellm-dashboard/src/components/PublicModelHubTableColumns.tsx

View workflow job for this annotation

GitHub Actions / ui

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
src={logo}
alt={provider}
className="size-3 shrink-0 object-contain"
Expand Down
6 changes: 6 additions & 0 deletions ui/litellm-dashboard/src/components/public_model_hub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
);
}

const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken, isEmbedded = false }) => {

Check warning on line 71 in ui/litellm-dashboard/src/components/public_model_hub.tsx

View workflow job for this annotation

GitHub Actions / ui

Arrow function has a complexity of 67. Maximum allowed is 20
const anchor = useComboboxAnchor();
const [proxyConfigured, setProxyConfigured] = useState<boolean>(false);
const [agentHubData, setAgentHubData] = useState<AgentCard[] | null>(null);
Expand Down Expand Up @@ -714,6 +714,12 @@
})}
</div>
</div>
{selectedModel.description && (
<div className="col-span-2">
<p className="font-medium">Description:</p>
<p className="mt-1">{selectedModel.description}</p>
</div>
)}
</div>

{/* Wildcard Routing Note */}
Expand Down
2 changes: 2 additions & 0 deletions ui/litellm-dashboard/src/lib/http/schema.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading