Repository navigation
revert: "feat(usage): search team keys beyond the top-N in the Team usage view (#42857)" #43377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,6 @@ | |
| import litellm | ||
| from litellm._logging import verbose_proxy_logger | ||
| from litellm._uuid import uuid | ||
| from litellm.constants import USAGE_TOP_API_KEYS_LIMIT | ||
| from litellm.integrations.prometheus import PrometheusLogger | ||
| from litellm.litellm_core_utils.safe_json_dumps import safe_dumps | ||
| from litellm.proxy._types import ( | ||
|
|
@@ -197,7 +196,6 @@ | |
| from litellm.router import Router | ||
| from litellm.types.proxy.auth.auth_checks import UserNotFoundError | ||
| from litellm.types.proxy.management_endpoints.common_daily_activity import ( | ||
| DailySpendMetadata, | ||
| SpendAnalyticsPaginatedResponse, | ||
| ) | ||
| from litellm.types.proxy.management_endpoints.team_endpoints import ( | ||
|
|
@@ -206,9 +204,7 @@ | |
| BulkUpdateTeamMemberPermissionsRequest, | ||
| BulkUpdateTeamMemberPermissionsResponse, | ||
| GetTeamMemberPermissionsResponse, | ||
| TeamIdSearchFilter, | ||
| TeamIdSearchMatch, | ||
| TeamKeyActivitySearchWhere, | ||
| TeamListItem, | ||
| TeamListResponse, | ||
| TeamMemberAddResult, | ||
|
|
@@ -6809,111 +6805,6 @@ async def get_team_daily_activity_aggregated( | |
| ) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
|
|
||
| def _team_key_search_where(*, search: str, scope: _TeamDailyActivityScope) -> TeamKeyActivitySearchWhere: | ||
| """Caller scoping lives inside the same Prisma where as the search term so `take` | ||
| never trims visible matches in favour of keys the caller is not allowed to see.""" | ||
| search_or: Final = ( | ||
| {"token": search}, # mutable-ok: prisma where clause leaf | ||
| {"key_alias": {"contains": search, "mode": "insensitive"}}, # mutable-ok: prisma where clause leaf | ||
| {"user_id": {"contains": search, "mode": "insensitive"}}, # mutable-ok: prisma where clause leaf | ||
| ) | ||
| own_keys: Final = tuple(scope.api_key_filter) if isinstance(scope.api_key_filter, list) else None | ||
| team_filter: Final[TeamIdSearchFilter | None] = ( | ||
| { # mutable-ok: prisma where clause leaf | ||
| "in": tuple(scope.team_ids), | ||
| "notIn": tuple(scope.exclude_team_ids), | ||
| } | ||
| if scope.team_ids is not None and scope.exclude_team_ids is not None | ||
| else {"in": tuple(scope.team_ids)} # mutable-ok: prisma where clause leaf | ||
| if scope.team_ids is not None | ||
| else {"notIn": tuple(scope.exclude_team_ids)} # mutable-ok: prisma where clause leaf | ||
| if scope.exclude_team_ids is not None | ||
| else None | ||
| ) | ||
| if team_filter is None and own_keys is None: | ||
| return {"OR": search_or} # mutable-ok: prisma where clause root | ||
| if team_filter is None and own_keys is not None: | ||
| return {"token": {"in": own_keys}, "OR": search_or} # mutable-ok: prisma where clause root | ||
| if team_filter is not None and own_keys is None: | ||
| return {"team_id": team_filter, "OR": search_or} # mutable-ok: prisma where clause root | ||
| assert team_filter is not None and own_keys is not None | ||
| return { # mutable-ok: prisma where clause root | ||
| "team_id": team_filter, | ||
| "token": {"in": own_keys}, # mutable-ok: prisma where clause leaf | ||
| "OR": search_or, | ||
| } | ||
|
|
||
|
|
||
| @router.get( | ||
| "/team/daily/activity/aggregated/search", | ||
| response_model=SpendAnalyticsPaginatedResponse, | ||
| tags=["team management"], # mutable-ok: FastAPI route tags shape | ||
| ) | ||
| async def search_team_daily_activity_keys( | ||
| user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], | ||
| search: str = fastapi.Query( | ||
| ..., | ||
| min_length=1, | ||
| description="Exact token hash, or a case-insensitive substring of the key alias or owning user id", | ||
| ), | ||
| team_ids: str | None = None, | ||
| start_date: str | None = None, | ||
| end_date: str | None = None, | ||
| exclude_team_ids: str | None = None, | ||
| timezone: int | None = None, | ||
| ) -> SpendAnalyticsPaginatedResponse: | ||
| """Aggregated daily team activity for the keys matching `search`, across every key the caller may | ||
| see rather than only the top USAGE_TOP_API_KEYS_LIMIT keys by spend.""" | ||
| from litellm.proxy.proxy_server import ( | ||
| prisma_client, | ||
| proxy_logging_obj, | ||
| user_api_key_cache, | ||
| ) | ||
|
|
||
| if prisma_client is None: | ||
| raise _daily_activity_error(status_code=500, message=CommonProxyErrors.db_not_connected_error.value) | ||
|
|
||
| range_error: Final = _aggregated_date_range_error(start_date, end_date) | ||
| if range_error is not None: | ||
| raise _daily_activity_error(status_code=400, message=range_error) | ||
|
|
||
| scope: Final = await _resolve_team_daily_activity_scope( | ||
| team_ids=team_ids, | ||
| exclude_team_ids=exclude_team_ids, | ||
| api_key=None, | ||
| user_api_key_dict=user_api_key_dict, | ||
| prisma_client=prisma_client, | ||
| user_api_key_cache=user_api_key_cache, | ||
| proxy_logging_obj=proxy_logging_obj, | ||
| ) | ||
| matched_keys: Final = await _tokens_db(prisma_client).find_many( | ||
| where=_team_key_search_where(search=search, scope=scope), | ||
| take=USAGE_TOP_API_KEYS_LIMIT, | ||
| order={"spend": "desc"}, # mutable-ok: prisma serializes order, keep it a plain dict | ||
| ) | ||
| tokens: Final = [key.token for key in matched_keys] # mutable-ok: get_daily_activity_aggregated takes list[str] | ||
| if not tokens: | ||
| return SpendAnalyticsPaginatedResponse( | ||
| results=[], # mutable-ok: response model field shape | ||
| metadata=DailySpendMetadata(api_key_limit=USAGE_TOP_API_KEYS_LIMIT, total_api_keys=0), | ||
| ) | ||
|
|
||
| return await get_daily_activity_aggregated( | ||
| prisma_client=prisma_client, | ||
| table_name="litellm_dailyteamspend", | ||
| entity_id_field="team_id", | ||
| entity_id=scope.team_ids, | ||
| entity_metadata_field=scope.team_alias_metadata, | ||
| start_date=start_date, | ||
| end_date=end_date, | ||
| model=None, | ||
| api_key=tokens, | ||
| exclude_entity_ids=scope.exclude_team_ids, | ||
| timezone_offset_minutes=timezone, | ||
| include_entity_breakdown=True, | ||
| ) | ||
|
|
||
|
|
||
| def _team_user_spend_sql(*, team_count: int, restrict_to_user: bool) -> str: | ||
| team_placeholders: Final = ", ".join(f"${i}" for i in range(3, 3 + team_count)) | ||
| user_clause: Final = f' AND sl."user" = ${3 + team_count}' if restrict_to_user else "" | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 0 additions & 128 deletions
128
tests/integration/spend/test_team_daily_activity_key_search.py
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/team/daily/activity/aggregated/searchreceive 404. The repository requires a user-controlled flag for backwards-incompatible changesRule Used: What: avoid backwards-incompatible changes without user-controlled flags Why: This breaks current behaviour for users using existing functionality Example of BAD: this PR (#22164) introduced run_post_custom... (source)