From 2d2f540480d34d8a2fd16a2877bd4e9ff5015b44 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 28 Apr 2026 16:58:03 +0530 Subject: [PATCH 01/14] feat(proxy): add team-level search provider credential resolution Allow search requests to resolve provider credentials from request metadata, team metadata, and default team settings with clear precedence, and expose this flow in proxy docs/UI with regression tests. Made-with: Cursor --- docs/my-website/docs/proxy/search.md | 92 ++++++++++++ docs/my-website/docs/proxy/team_budgets.md | 58 ++++++++ litellm/proxy/_types.py | 31 +++- .../management_endpoints/team_endpoints.py | 110 ++++++++++++++- litellm/proxy/search_endpoints/endpoints.py | 10 ++ litellm/router_utils/search_api_router.py | 127 ++++++++++++++++- .../test_team_search_credentials.py | 133 ++++++++++++++++++ .../src/components/networking.tsx | 34 +++++ .../src/components/team/TeamInfo.tsx | 53 ++++++- 9 files changed, 638 insertions(+), 10 deletions(-) create mode 100644 docs/my-website/docs/proxy/search.md create mode 100644 docs/my-website/docs/proxy/team_budgets.md create mode 100644 tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py diff --git a/docs/my-website/docs/proxy/search.md b/docs/my-website/docs/proxy/search.md new file mode 100644 index 0000000000..65c431eb53 --- /dev/null +++ b/docs/my-website/docs/proxy/search.md @@ -0,0 +1,92 @@ +# Search API + +LiteLLM supports team-aware search provider credentials for providers like Tavily, Perplexity, Brave, Exa, and Serper. + +## Per-team search provider configuration + +Set per-team credentials in team metadata: + +```json +{ + "search_provider_config": { + "tavily": { + "api_key": "tvly-team-a-key", + "api_base": "https://api.tavily.com" + }, + "perplexity": { + "api_key": "pplx-team-a-key" + } + } +} +``` + +Update via API: + +```bash +curl -X POST "http://localhost:4000/team/search_provider_config/update" \ + -H "Authorization: Bearer sk-admin-key" \ + -H "Content-Type: application/json" \ + -d '{ + "team_id": "team-a", + "provider": "tavily", + "api_key": "tvly-team-a-key", + "api_base": "https://api.tavily.com" + }' +``` + +## Request flow and precedence + +Search credentials resolve in this order: + +1. Request metadata: `metadata.search_provider_config.` +2. Team DB metadata: `user_api_key_team_metadata.search_provider_config.` +3. YAML team settings: `default_team_settings[].search_provider_config.` +4. Search tool config: `search_tools[].litellm_params` +5. Provider env fallback (`TAVILY_API_KEY`, etc.) + +## Calling search as an end-user + +The caller only uses their team-bound virtual key. + +```bash +curl -X POST "http://localhost:4000/v1/search" \ + -H "Authorization: Bearer sk-team-a-user-key" \ + -H "Content-Type: application/json" \ + -d '{ + "search_tool_name": "company-search", + "query": "latest AI news", + "max_results": 5 + }' +``` + +or with URL tool name: + +```bash +curl -X POST "http://localhost:4000/v1/search/company-search" \ + -H "Authorization: Bearer sk-team-a-user-key" \ + -H "Content-Type: application/json" \ + -d '{ + "query": "latest AI news", + "max_results": 5 + }' +``` + +## YAML examples + +```yaml +search_tools: + - search_tool_name: company-search + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_DEFAULT_API_KEY + +default_team_settings: + - team_id: team-a + search_provider_config: + tavily: + api_key: os.environ/TAVILY_TEAM_A_API_KEY + - team_id: team-b + search_provider_config: + tavily: + api_key: os.environ/TAVILY_TEAM_B_API_KEY +``` diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md new file mode 100644 index 0000000000..47f3a832b0 --- /dev/null +++ b/docs/my-website/docs/proxy/team_budgets.md @@ -0,0 +1,58 @@ +# Team Budgets and Search Cost Attribution + +When search requests are made through LiteLLM with a team-bound key, spend is attributed to that team. + +## Cost attribution for search + +Search calls (`search` / `asearch`) are logged with: + +- `metadata.user_api_key_team_id` +- spend rows in `LiteLLM_SpendLogs.team_id` + +This means each team's search usage can be queried independently even when using the same model/provider family. + +## Why per-team search keys matter + +Using one shared Tavily key makes upstream provider billing opaque by team. +With team-specific provider keys: + +- provider-side billing is isolated per team +- LiteLLM spend logs still aggregate by team id +- finance can reconcile provider invoices + LiteLLM spend logs + +## Recommended setup + +1. Issue per-team virtual keys in LiteLLM. +2. Configure `metadata.search_provider_config` per team. +3. Keep a fallback tool-level key only for teams without explicit config. + +## Example team update + +```bash +curl -X POST "http://localhost:4000/team/update" \ + -H "Authorization: Bearer sk-admin-key" \ + -H "Content-Type: application/json" \ + -d '{ + "team_id": "team-research", + "metadata": { + "search_provider_config": { + "tavily": { + "api_key": "tvly-research-key" + }, + "perplexity": { + "api_key": "pplx-research-key" + } + } + } + }' +``` + +## Example spend query + +```sql +SELECT team_id, call_type, SUM(spend) AS total_spend, COUNT(*) AS requests +FROM "LiteLLM_SpendLogs" +WHERE call_type IN ('search', 'asearch') +GROUP BY team_id, call_type +ORDER BY total_spend DESC; +``` diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 92c920ca59..7b92af92f2 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -1695,6 +1695,28 @@ class OrgMember(MemberBase): ] +class SearchProviderCredentials(LiteLLMPydanticObjectBase): + """ + Per-team credentials for a search provider. + """ + + api_key: Optional[str] = None + api_base: Optional[str] = None + + +class TeamSearchProviderConfig(LiteLLMPydanticObjectBase): + """ + Structured team-level search provider credentials. + Stored in team metadata under `search_provider_config`. + """ + + tavily: Optional[SearchProviderCredentials] = None + perplexity: Optional[SearchProviderCredentials] = None + brave: Optional[SearchProviderCredentials] = None + exa: Optional[SearchProviderCredentials] = None + serper: Optional[SearchProviderCredentials] = None + + class TeamBase(LiteLLMPydanticObjectBase): team_alias: Optional[str] = None team_id: Optional[str] = None @@ -1703,7 +1725,7 @@ class TeamBase(LiteLLMPydanticObjectBase): members: list = [] members_with_roles: List[Member] = [] team_member_permissions: Optional[List[str]] = None - metadata: Optional[dict] = None + metadata: Optional[dict] = None # may include search_provider_config tpm_limit: Optional[int] = None rpm_limit: Optional[int] = None @@ -1823,6 +1845,13 @@ class UpdateTeamRequest(LiteLLMPydanticObjectBase): ) +class TeamSearchProviderConfigUpdateRequest(LiteLLMPydanticObjectBase): + team_id: str + provider: str + api_key: Optional[str] = None + api_base: Optional[str] = None + + class ResetTeamBudgetRequest(LiteLLMPydanticObjectBase): """ internal type used to reset the budget on a team diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index f254fea3e7..abd5239c9a 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -59,6 +59,7 @@ from litellm.proxy._types import ( TeamMemberUpdateResponse, TeamModelAddRequest, TeamModelDeleteRequest, + TeamSearchProviderConfigUpdateRequest, UpdateTeamRequest, UserAPIKeyAuth, ) @@ -1856,6 +1857,108 @@ async def update_team( # noqa: PLR0915 raise handle_exception_on_proxy(e) +@router.post( + "/team/search_provider_config/update", + tags=["team management"], + dependencies=[Depends(user_api_key_auth)], +) +@management_endpoint_wrapper +async def update_team_search_provider_config( + data: TeamSearchProviderConfigUpdateRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Update per-team search provider credentials in team metadata. + + Stored under: + metadata.search_provider_config..{api_key, api_base} + """ + from litellm.proxy.auth.auth_checks import _cache_team_object + from litellm.proxy.proxy_server import ( + prisma_client, + proxy_logging_obj, + user_api_key_cache, + ) + + if prisma_client is None: + raise HTTPException( + status_code=500, + detail={"error": CommonProxyErrors.db_not_connected_error.value}, + ) + + provider = data.provider.strip().lower() + if provider == "": + raise HTTPException( + status_code=400, detail={"error": "provider cannot be empty"} + ) + + existing_team_row = await prisma_client.db.litellm_teamtable.find_unique( + where={"team_id": data.team_id} + ) + if existing_team_row is None: + raise HTTPException( + status_code=404, + detail={"error": f"Team not found, passed team_id={data.team_id}"}, + ) + + await _verify_team_access( + team_obj=LiteLLM_TeamTable(**existing_team_row.model_dump()), + user_api_key_dict=user_api_key_dict, + ) + + metadata: Dict[str, Any] = {} + if isinstance(existing_team_row.metadata, dict): + metadata = dict(existing_team_row.metadata) + + search_provider_config = metadata.get("search_provider_config") + if not isinstance(search_provider_config, dict): + search_provider_config = {} + + provider_config = search_provider_config.get(provider) + if not isinstance(provider_config, dict): + provider_config = {} + + if data.api_key is not None: + provider_config["api_key"] = data.api_key + if data.api_base is not None: + provider_config["api_base"] = data.api_base + + if provider_config.get("api_key") in (None, "") and provider_config.get( + "api_base" + ) in ( + None, + "", + ): + search_provider_config.pop(provider, None) + else: + search_provider_config[provider] = provider_config + + metadata["search_provider_config"] = search_provider_config + + team_row: Optional[LiteLLM_TeamTable] = ( + await prisma_client.db.litellm_teamtable.update( + where={"team_id": data.team_id}, + data={"metadata": metadata}, + include={"litellm_model_table": True}, # type: ignore + ) + ) + + if team_row is not None and team_row.team_id is not None: + await _cache_team_object( + team_id=team_row.team_id, + team_table=LiteLLM_TeamTableCachedObj(**team_row.model_dump()), + user_api_key_cache=user_api_key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + + return { + "message": "Team search provider configuration updated", + "team_id": data.team_id, + "provider": provider, + "search_provider_config": search_provider_config, + } + + def _set_budget_reset_at(data: UpdateTeamRequest, updated_kv: dict) -> None: """Set budget_reset_at in updated_kv if budget_duration is provided.""" if data.budget_duration is not None: @@ -2049,8 +2152,11 @@ async def _process_team_members( # Resolve allowed_models: explicit request value, or fall back to team's default_team_member_models member_allowed_models = data.allowed_models - if member_allowed_models is None and complete_team_data.default_team_member_models: - member_allowed_models = complete_team_data.default_team_member_models + team_default_member_models = getattr( + complete_team_data, "default_team_member_models", None + ) + if member_allowed_models is None and team_default_member_models: + member_allowed_models = team_default_member_models if isinstance(data.member, Member): try: diff --git a/litellm/proxy/search_endpoints/endpoints.py b/litellm/proxy/search_endpoints/endpoints.py index 8bed5b5407..ce2949f707 100644 --- a/litellm/proxy/search_endpoints/endpoints.py +++ b/litellm/proxy/search_endpoints/endpoints.py @@ -163,6 +163,16 @@ async def search( data["metadata"] = {} data["metadata"]["model_group"] = search_tool_name_value + # Ensure team context is available to search router credential resolution. + # add_litellm_data_to_request() also injects these values, but this keeps + # search endpoint behavior explicit and resilient for direct router paths. + if "metadata" not in data or not isinstance(data.get("metadata"), dict): + data["metadata"] = {} + if getattr(user_api_key_dict, "team_metadata", None) is not None: + data["metadata"]["user_api_key_team_metadata"] = user_api_key_dict.team_metadata + if getattr(user_api_key_dict, "team_id", None) is not None: + data["metadata"]["user_api_key_team_id"] = user_api_key_dict.team_id + # Process request using ProxyBaseLLMRequestProcessing processor = ProxyBaseLLMRequestProcessing(data=data) try: diff --git a/litellm/router_utils/search_api_router.py b/litellm/router_utils/search_api_router.py index a26aa7e71e..e2e98a6573 100644 --- a/litellm/router_utils/search_api_router.py +++ b/litellm/router_utils/search_api_router.py @@ -8,8 +8,9 @@ import asyncio import random import traceback from functools import partial -from typing import Any, Callable +from typing import Any, Callable, Dict, Optional, Tuple +import litellm from litellm._logging import verbose_router_logger @@ -20,6 +21,96 @@ class SearchAPIRouter: Provides methods for search tool selection, load balancing, and fallback handling. """ + @staticmethod + def _get_team_config_from_default_settings( + team_id: Optional[str], + ) -> Optional[Dict[str, Any]]: + """ + Resolve team config from litellm.default_team_settings. + + This allows search requests to read per-team settings from proxy config + (YAML) similar to completion paths that use ProxyConfig.load_team_config(). + """ + if not team_id: + return None + + default_team_settings = getattr(litellm, "default_team_settings", None) + if not isinstance(default_team_settings, list): + return None + + for team_setting in default_team_settings: + if ( + isinstance(team_setting, dict) + and team_setting.get("team_id") == team_id + ): + return team_setting + return None + + @staticmethod + def _resolve_search_provider_credentials( + *, + search_provider: str, + tool_litellm_params: Dict[str, Any], + request_metadata: Optional[Dict[str, Any]] = None, + team_metadata: Optional[Dict[str, Any]] = None, + team_config: Optional[Dict[str, Any]] = None, + ) -> Tuple[Optional[str], Optional[str]]: + """ + Resolve search provider credentials with precedence: + 1. request metadata.search_provider_config.{provider} + 2. team metadata.search_provider_config.{provider} + 3. default_team_settings.search_provider_config.{provider} + 4. search_tool.litellm_params + 5. env fallback in provider validate_environment() + """ + resolved_api_key: Optional[str] = None + resolved_api_base: Optional[str] = None + + request_provider_config = {} + if isinstance(request_metadata, dict): + search_provider_config = request_metadata.get("search_provider_config") + if isinstance(search_provider_config, dict): + request_provider_config = search_provider_config.get( + search_provider, {} + ) + + team_provider_config = {} + if isinstance(team_metadata, dict): + search_provider_config = team_metadata.get("search_provider_config") + if isinstance(search_provider_config, dict): + team_provider_config = search_provider_config.get(search_provider, {}) + + team_settings_provider_config = {} + if isinstance(team_config, dict): + search_provider_config = team_config.get("search_provider_config") + if isinstance(search_provider_config, dict): + team_settings_provider_config = search_provider_config.get( + search_provider, {} + ) + + if isinstance(request_provider_config, dict): + resolved_api_key = request_provider_config.get("api_key") + resolved_api_base = request_provider_config.get("api_base") + + if resolved_api_key is None and isinstance(team_provider_config, dict): + resolved_api_key = team_provider_config.get("api_key") + if resolved_api_base is None and isinstance(team_provider_config, dict): + resolved_api_base = team_provider_config.get("api_base") + + if resolved_api_key is None and isinstance(team_settings_provider_config, dict): + resolved_api_key = team_settings_provider_config.get("api_key") + if resolved_api_base is None and isinstance( + team_settings_provider_config, dict + ): + resolved_api_base = team_settings_provider_config.get("api_base") + + if resolved_api_key is None: + resolved_api_key = tool_litellm_params.get("api_key") + if resolved_api_base is None: + resolved_api_base = tool_litellm_params.get("api_base") + + return resolved_api_key, resolved_api_base + @staticmethod async def update_router_search_tools(router_instance: Any, search_tools: list): """ @@ -198,16 +289,42 @@ class SearchAPIRouter: # Extract search provider and other params from litellm_params litellm_params = selected_tool.get("litellm_params", {}) search_provider = litellm_params.get("search_provider") - api_key = litellm_params.get("api_key") - api_base = litellm_params.get("api_base") - if not search_provider: raise ValueError( f"search_provider not found in litellm_params for search tool '{search_tool_name}'" ) + request_metadata = kwargs.get("metadata") + litellm_metadata = kwargs.get("litellm_metadata") + if not isinstance(request_metadata, dict) and isinstance( + litellm_metadata, dict + ): + request_metadata = litellm_metadata + + team_metadata = {} + team_id: Optional[str] = None + if isinstance(request_metadata, dict): + _team_metadata = request_metadata.get("user_api_key_team_metadata") + if isinstance(_team_metadata, dict): + team_metadata = _team_metadata + _team_id = request_metadata.get("user_api_key_team_id") + if isinstance(_team_id, str): + team_id = _team_id + + team_config = SearchAPIRouter._get_team_config_from_default_settings( + team_id=team_id + ) + + api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( + search_provider=search_provider, + tool_litellm_params=litellm_params, + request_metadata=request_metadata, + team_metadata=team_metadata, + team_config=team_config, + ) + verbose_router_logger.debug( - f"Selected search tool with provider: {search_provider}" + f"Selected search tool with provider: {search_provider}, team_id={team_id}" ) # Call the original search function with the provider config diff --git a/tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py b/tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py new file mode 100644 index 0000000000..eab167fb75 --- /dev/null +++ b/tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py @@ -0,0 +1,133 @@ +import os +import sys +from unittest.mock import patch + +import pytest +from fastapi.testclient import TestClient + +sys.path.insert(0, os.path.abspath("../../../../..")) + +from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth +from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.proxy.proxy_server import app +from litellm.router_utils.search_api_router import SearchAPIRouter + + +def test_resolve_credentials_team_metadata_overrides_tool_params(): + api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( + search_provider="tavily", + tool_litellm_params={ + "api_key": "tool-key", + "api_base": "https://tool.example.com", + }, + team_metadata={ + "search_provider_config": { + "tavily": { + "api_key": "team-key", + "api_base": "https://team.example.com", + } + } + }, + ) + assert api_key == "team-key" + assert api_base == "https://team.example.com" + + +def test_resolve_credentials_request_metadata_has_highest_precedence(): + api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( + search_provider="tavily", + tool_litellm_params={ + "api_key": "tool-key", + "api_base": "https://tool.example.com", + }, + request_metadata={ + "search_provider_config": { + "tavily": { + "api_key": "request-key", + "api_base": "https://request.example.com", + } + } + }, + team_metadata={ + "search_provider_config": { + "tavily": { + "api_key": "team-key", + "api_base": "https://team.example.com", + } + } + }, + ) + assert api_key == "request-key" + assert api_base == "https://request.example.com" + + +def test_resolve_credentials_from_default_team_settings(): + with patch( + "litellm.default_team_settings", + [ + { + "team_id": "team-a", + "search_provider_config": { + "tavily": { + "api_key": "team-settings-key", + "api_base": "https://team-settings.example.com", + } + }, + } + ], + ): + team_config = SearchAPIRouter._get_team_config_from_default_settings("team-a") + api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( + search_provider="tavily", + tool_litellm_params={}, + team_config=team_config, + ) + assert api_key == "team-settings-key" + assert api_base == "https://team-settings.example.com" + + +@pytest.mark.asyncio +async def test_search_endpoint_injects_team_metadata(): + captured_metadata = {} + + async def _mock_process(self, **kwargs): + nonlocal captured_metadata + captured_metadata = self.data.get("metadata", {}) + return {"object": "search", "results": []} + + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, + user_id="admin-user", + team_id="team-test", + team_metadata={ + "search_provider_config": { + "tavily": {"api_key": "team-test-key"}, + } + }, + ) + + try: + with patch( + "litellm.proxy.common_request_processing.ProxyBaseLLMRequestProcessing.base_process_llm_request", + new=_mock_process, + ): + client = TestClient(app) + response = client.post( + "/v1/search", + json={ + "search_tool_name": "tool-a", + "search_provider": "tavily", + "query": "latest ai news", + }, + ) + assert response.status_code == 200 + assert captured_metadata.get("user_api_key_team_id") == "team-test" + assert ( + captured_metadata.get("user_api_key_team_metadata", {}) + .get("search_provider_config", {}) + .get("tavily", {}) + .get("api_key") + == "team-test-key" + ) + finally: + app.dependency_overrides.pop(user_api_key_auth, None) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 44208904a7..6cd9423c37 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -3725,6 +3725,40 @@ export const teamUpdateCall = async ( } }; +export const updateTeamSearchProviderConfigCall = async ( + accessToken: string, + formValues: { + team_id: string; + provider: string; + api_key?: string | null; + api_base?: string | null; + }, +) => { + try { + const url = proxyBaseUrl + ? `${proxyBaseUrl}/team/search_provider_config/update` + : `/team/search_provider_config/update`; + const response = await fetch(url, { + method: "POST", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(formValues), + }); + + if (!response.ok) { + const errorData = await response.text(); + handleError(errorData); + throw new Error(errorData); + } + return await response.json(); + } catch (error) { + console.error("Failed to update team search provider config:", error); + throw error; + } +}; + /** * Patch update a model * diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index ef98054c08..1a2e97faaa 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -457,13 +457,26 @@ const TeamInfoView: React.FC = ({ try { const rawMetadata = values.metadata ? JSON.parse(values.metadata) : {}; // Exclude soft_budget_alerting_emails from parsed metadata since it's handled separately - const { soft_budget_alerting_emails, ...rest } = rawMetadata; + const { soft_budget_alerting_emails, search_provider_config, ...rest } = rawMetadata; parsedMetadata = rest; } catch (e) { NotificationsManager.fromBackend("Invalid JSON in metadata field"); return; } + let searchProviderConfig: Record | undefined; + if (typeof values.search_provider_config === "string") { + const trimmedSearchProviderConfig = values.search_provider_config.trim(); + if (trimmedSearchProviderConfig.length > 0) { + try { + searchProviderConfig = JSON.parse(trimmedSearchProviderConfig); + } catch (e) { + NotificationsManager.fromBackend("Invalid JSON in search provider configuration"); + return; + } + } + } + let secretManagerSettings: Record | undefined; if (typeof values.secret_manager_settings === "string") { const trimmedSecretConfig = values.secret_manager_settings.trim(); @@ -513,6 +526,7 @@ const TeamInfoView: React.FC = ({ budget_duration: values.budget_duration, metadata: { ...parsedMetadata, + ...(searchProviderConfig !== undefined ? { search_provider_config: searchProviderConfig } : {}), guardrails: (values.guardrails || []).filter((n: string) => !globalGuardrailNames.has(n)), opted_out_global_guardrails: optedOutGlobalGuardrails, ...(values.logging_settings?.length > 0 ? { logging: values.logging_settings } : {}), @@ -952,11 +966,14 @@ const TeamInfoView: React.FC = ({ : "", metadata: info.metadata ? JSON.stringify( - (({ logging, secret_manager_settings, soft_budget_alerting_emails, model_tpm_limit, model_rpm_limit, ...rest }) => rest)(info.metadata), + (({ logging, secret_manager_settings, soft_budget_alerting_emails, search_provider_config, model_tpm_limit, model_rpm_limit, ...rest }) => rest)(info.metadata), null, 2, ) : "", + search_provider_config: info.metadata?.search_provider_config + ? JSON.stringify(info.metadata.search_provider_config, null, 2) + : "", logging_settings: info.metadata?.logging || [], secret_manager_settings: info.metadata?.secret_manager_settings ? JSON.stringify(info.metadata.secret_manager_settings, null, 2) @@ -1399,6 +1416,29 @@ const TeamInfoView: React.FC = ({ /> + { + if (!value || (typeof value === "string" && value.trim() === "")) { + return Promise.resolve(); + } + try { + JSON.parse(value); + return Promise.resolve(); + } catch (error) { + return Promise.reject(new Error("Please enter valid JSON")); + } + }, + }, + ]} + > + + + = ({ )} + + {info.metadata?.search_provider_config && ( +
+ Search Provider Configuration +
+                          {JSON.stringify(info.metadata.search_provider_config, null, 2)}
+                        
+
+ )} )} From c4e074f27707a509f76801d8532a9982b3643567 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 28 Apr 2026 18:53:32 +0530 Subject: [PATCH 02/14] feat(proxy): add model-like search tool access control Treat search tools like models by adding team/key allowed_search_tools controls, enforcing search tool authorization checks, and moving credential ownership to search tool config only to avoid exposing secrets in team metadata. Made-with: Cursor --- .../docs/proxy/search_tools_access.md | 439 ++++++++++++ .../out/{404.html => 404/index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../out/{chat.html => chat/index.html} | 0 .../index.html} | 0 .../{budgets.html => budgets/index.html} | 0 .../{caching.html => caching/index.html} | 0 .../index.html} | 0 .../{old-usage.html => old-usage/index.html} | 0 .../{prompts.html => prompts/index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../out/{login.html => login/index.html} | 0 .../out/{logs.html => logs/index.html} | 0 .../{callback.html => callback/index.html} | 0 .../{model-hub.html => model-hub/index.html} | 0 .../{model_hub.html => model_hub/index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../{policies.html => policies/index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../{ui-theme.html => ui-theme/index.html} | 0 .../out/{skills.html => skills/index.html} | 0 .../out/{teams.html => teams/index.html} | 0 .../{test-key.html => test-key/index.html} | 0 .../index.html} | 0 .../index.html} | 0 .../out/{usage.html => usage/index.html} | 0 .../out/{users.html => users/index.html} | 0 .../index.html} | 0 litellm/proxy/_types.py | 11 +- litellm/proxy/auth/auth_checks.py | 94 +++ .../management_endpoints/team_endpoints.py | 103 --- litellm/proxy/schema.prisma | 2 + litellm/proxy/search_endpoints/endpoints.py | 35 +- litellm/router_utils/search_api_router.py | 63 +- proxy_server.log | 659 ++++++++++++++++++ proxy_server_config.yaml | 232 +----- tests/test_proxy_search_tool_auth.py | 212 ++++++ .../components/modals/CreateTeamModal.tsx | 50 +- .../src/components/OldTeams.tsx | 49 +- .../src/components/networking.tsx | 34 - .../src/components/team/TeamInfo.tsx | 92 ++- 49 files changed, 1597 insertions(+), 478 deletions(-) create mode 100644 docs/my-website/docs/proxy/search_tools_access.md rename litellm/proxy/_experimental/out/{404.html => 404/index.html} (100%) rename litellm/proxy/_experimental/out/{_not-found.html => _not-found/index.html} (100%) rename litellm/proxy/_experimental/out/{api-reference.html => api-reference/index.html} (100%) rename litellm/proxy/_experimental/out/{chat.html => chat/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{api-playground.html => api-playground/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{budgets.html => budgets/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{caching.html => caching/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{claude-code-plugins.html => claude-code-plugins/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{old-usage.html => old-usage/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{prompts.html => prompts/index.html} (100%) rename litellm/proxy/_experimental/out/experimental/{tag-management.html => tag-management/index.html} (100%) rename litellm/proxy/_experimental/out/{guardrails.html => guardrails/index.html} (100%) rename litellm/proxy/_experimental/out/{login.html => login/index.html} (100%) rename litellm/proxy/_experimental/out/{logs.html => logs/index.html} (100%) rename litellm/proxy/_experimental/out/mcp/oauth/{callback.html => callback/index.html} (100%) rename litellm/proxy/_experimental/out/{model-hub.html => model-hub/index.html} (100%) rename litellm/proxy/_experimental/out/{model_hub.html => model_hub/index.html} (100%) rename litellm/proxy/_experimental/out/{model_hub_table.html => model_hub_table/index.html} (100%) rename litellm/proxy/_experimental/out/{models-and-endpoints.html => models-and-endpoints/index.html} (100%) rename litellm/proxy/_experimental/out/{onboarding.html => onboarding/index.html} (100%) rename litellm/proxy/_experimental/out/{organizations.html => organizations/index.html} (100%) rename litellm/proxy/_experimental/out/{playground.html => playground/index.html} (100%) rename litellm/proxy/_experimental/out/{policies.html => policies/index.html} (100%) rename litellm/proxy/_experimental/out/settings/{admin-settings.html => admin-settings/index.html} (100%) rename litellm/proxy/_experimental/out/settings/{logging-and-alerts.html => logging-and-alerts/index.html} (100%) rename litellm/proxy/_experimental/out/settings/{router-settings.html => router-settings/index.html} (100%) rename litellm/proxy/_experimental/out/settings/{ui-theme.html => ui-theme/index.html} (100%) rename litellm/proxy/_experimental/out/{skills.html => skills/index.html} (100%) rename litellm/proxy/_experimental/out/{teams.html => teams/index.html} (100%) rename litellm/proxy/_experimental/out/{test-key.html => test-key/index.html} (100%) rename litellm/proxy/_experimental/out/tools/{mcp-servers.html => mcp-servers/index.html} (100%) rename litellm/proxy/_experimental/out/tools/{vector-stores.html => vector-stores/index.html} (100%) rename litellm/proxy/_experimental/out/{usage.html => usage/index.html} (100%) rename litellm/proxy/_experimental/out/{users.html => users/index.html} (100%) rename litellm/proxy/_experimental/out/{virtual-keys.html => virtual-keys/index.html} (100%) create mode 100644 proxy_server.log create mode 100644 tests/test_proxy_search_tool_auth.py diff --git a/docs/my-website/docs/proxy/search_tools_access.md b/docs/my-website/docs/proxy/search_tools_access.md new file mode 100644 index 0000000000..8c04735363 --- /dev/null +++ b/docs/my-website/docs/proxy/search_tools_access.md @@ -0,0 +1,439 @@ +# Search Tools Access Control + +Control which teams and keys can access specific search tools using model-like allowlists. + +## Overview + +Search tools in LiteLLM Proxy use the same access control pattern as models: + +- **Team-level allowlist**: `allowed_search_tools` on teams +- **Key-level allowlist**: `allowed_search_tools` on keys +- **Tool-only credentials**: API keys stored ONLY in search tool configuration +- **Secure by default**: Credentials never exposed in team/key metadata + +## Quick Start + +### Step 1: Configure Search Tools + +Define search tools in your `proxy_server_config.yaml`: + +```yaml +search_tools: + - search_tool_name: perplexity-search + litellm_params: + search_provider: perplexity + api_key: os.environ/PERPLEXITYAI_API_KEY + + - search_tool_name: tavily-search + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_API_KEY + + - search_tool_name: tavily-marketing + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_MARKETING_API_KEY + + - search_tool_name: brave-search + litellm_params: + search_provider: brave + api_key: os.environ/BRAVE_API_KEY +``` + +### Step 2: Create Teams with Search Tool Access + +```bash +curl -X POST 'http://localhost:4000/team/new' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + -d '{ + "team_alias": "marketing-team", + "models": ["gpt-4"], + "allowed_search_tools": ["tavily-marketing", "perplexity-search"] + }' +``` + +### Step 3: Generate Keys for Teams + +```bash +curl -X POST 'http://localhost:4000/key/generate' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + -d '{ + "team_id": "", + "models": ["gpt-4"], + "allowed_search_tools": ["tavily-marketing"] + }' +``` + +### Step 4: Use Search Tools + +```bash +curl -X POST 'http://localhost:4000/v1/search/tavily-marketing' \ + -H 'Authorization: Bearer sk-...' \ + -d '{"query": "latest marketing trends"}' +``` + +## Access Control Rules + +### Authorization Flow + +```mermaid +flowchart TD + Request["/v1/search/tavily-search"] --> KeyCheck{Key has access?} + KeyCheck -->|No| Deny403[403 Forbidden] + KeyCheck -->|Yes| TeamCheck{Team has access?} + TeamCheck -->|No| Deny403 + TeamCheck -->|Yes| GetCreds[Get credentials from tool config] + GetCreds --> CallAPI[Call Tavily API] +``` + +### Allowlist Behavior + +| Allowlist Value | Behavior | +|----------------|----------| +| `[]` (empty) | Access to **all** search tools | +| `["tool-a", "tool-b"]` | Access only to `tool-a` and `tool-b` | +| Not set / `null` | Access to **all** search tools | + +### Examples + +**Example 1: Team restricts tools, key further restricts** + +```yaml +# Team allows 3 tools +team.allowed_search_tools = ["tavily", "perplexity", "brave"] + +# Key only allows 1 tool +key.allowed_search_tools = ["tavily"] + +# Result: Key can ONLY access "tavily" +``` + +**Example 2: Empty allowlists grant full access** + +```yaml +# Team allows all +team.allowed_search_tools = [] + +# Key allows all +key.allowed_search_tools = [] + +# Result: Key can access ANY search tool +``` + +**Example 3: Team blocks access even if key allows** + +```yaml +# Team restricts to perplexity +team.allowed_search_tools = ["perplexity"] + +# Key allows tavily +key.allowed_search_tools = ["tavily"] + +# Result: Access DENIED - team doesn't allow tavily +``` + +## Configuration Patterns + +### Pattern 1: Per-Team Search Tool Isolation + +Each team gets their own search tool with dedicated credentials: + +```yaml +search_tools: + - search_tool_name: tavily-team-a + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_TEAM_A_KEY + + - search_tool_name: tavily-team-b + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_TEAM_B_KEY +``` + +```bash +# Create teams with isolated tools +curl -X POST 'http://localhost:4000/team/new' \ + -H 'Authorization: Bearer ' \ + -d '{ + "team_alias": "team-a", + "allowed_search_tools": ["tavily-team-a"] + }' +``` + +**Benefits**: +- Complete cost isolation (different Tavily accounts) +- Separate rate limits per team +- Independent billing + +### Pattern 2: Shared Tools with Access Control + +Share search tools across teams with allowlist restrictions: + +```yaml +search_tools: + - search_tool_name: tavily-premium + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_PREMIUM_KEY + + - search_tool_name: perplexity-standard + litellm_params: + search_provider: perplexity + api_key: os.environ/PERPLEXITY_KEY +``` + +```bash +# Enterprise team gets premium tools +curl -X POST 'http://localhost:4000/team/new' \ + -d '{ + "team_alias": "enterprise", + "allowed_search_tools": ["tavily-premium", "perplexity-standard"] + }' + +# Regular team gets standard tools only +curl -X POST 'http://localhost:4000/team/new' \ + -d '{ + "team_alias": "standard", + "allowed_search_tools": ["perplexity-standard"] + }' +``` + +### Pattern 3: Open Access with Cost Tracking + +Allow all teams to access tools, track costs via `team_id`: + +```yaml +search_tools: + - search_tool_name: tavily-shared + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_SHARED_KEY +``` + +```bash +# Teams with empty allowlists can access all tools +curl -X POST 'http://localhost:4000/team/new' \ + -d '{ + "team_alias": "team-a", + "allowed_search_tools": [] + }' +``` + +Query spend by team: + +```sql +SELECT + team_id, + SUM(spend) as total_spend, + COUNT(*) as request_count +FROM "LiteLLM_SpendLogs" +WHERE call_type = 'search' + AND model LIKE 'tavily%' +GROUP BY team_id; +``` + +## Security Model + +### Credentials Storage + +**Secure**: Credentials stored ONLY in search tool configuration + +```yaml +# ✅ CORRECT - Credentials in tool config +search_tools: + - search_tool_name: tavily-search + litellm_params: + api_key: os.environ/TAVILY_API_KEY # Stored here +``` + +**Never in team/key metadata**: + +```json +{ + "team_id": "team-123", + "allowed_search_tools": ["tavily-search"], + "metadata": {} // ✅ No credentials here +} +``` + +### Access Control Only + +Teams and keys only specify **which tools** they can access, not credentials: + +```json +{ + "team": { + "allowed_search_tools": ["tool-a", "tool-b"] // Access control + }, + "key": { + "allowed_search_tools": ["tool-a"] // Access control + } +} +``` + +## API Reference + +### Create Team with Search Tools + +```bash +POST /team/new + +{ + "team_alias": "marketing", + "models": ["gpt-4"], + "allowed_search_tools": ["tavily-search", "perplexity-search"] +} +``` + +### Update Team Search Tools + +```bash +POST /team/update + +{ + "team_id": "team-123", + "allowed_search_tools": ["brave-search"] +} +``` + +### Generate Key with Search Tools + +```bash +POST /key/generate + +{ + "team_id": "team-123", + "models": ["gpt-4"], + "allowed_search_tools": ["tavily-search"] +} +``` + +### List Available Search Tools + +```bash +GET /v1/search/tools + +# Response: +{ + "object": "list", + "data": [ + { + "search_tool_name": "tavily-search", + "search_provider": "tavily" + } + ] +} +``` + +## Cost Attribution + +Search requests are automatically attributed to the team via `team_id` in spend logs: + +```sql +SELECT + team_id, + model as search_tool, + SUM(spend) as cost, + COUNT(*) as requests +FROM "LiteLLM_SpendLogs" +WHERE call_type = 'search' + AND created_at >= NOW() - INTERVAL '30 days' +GROUP BY team_id, model +ORDER BY cost DESC; +``` + +**Example output**: + +| team_id | search_tool | cost | requests | +|---------|-------------|------|----------| +| team-marketing | tavily-search | $45.20 | 904 | +| team-engineering | perplexity-search | $32.15 | 643 | +| team-research | brave-search | $8.50 | 170 | + +## Migration from Legacy Approach + +If you previously stored credentials in team metadata, migrate to the new approach: + +### Before (Insecure) + +```json +{ + "team": { + "metadata": { + "search_provider_config": { + "tavily": {"api_key": "tvly-..."} // ❌ Exposed + } + } + } +} +``` + +### After (Secure) + +```yaml +# 1. Move credentials to search tool config +search_tools: + - search_tool_name: tavily-marketing + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_MARKETING_KEY # ✅ Secure + +# 2. Update team with allowlist +team: + allowed_search_tools: ["tavily-marketing"] # ✅ Access control only +``` + +## Troubleshooting + +### 403 Forbidden Error + +```json +{ + "error": "Key not allowed to access search tool: tavily-search. + Allowed search tools: [perplexity-search]" +} +``` + +**Solution**: Add the search tool to key's `allowed_search_tools`: + +```bash +curl -X POST 'http://localhost:4000/key/update' \ + -d '{ + "key": "sk-...", + "allowed_search_tools": ["tavily-search", "perplexity-search"] + }' +``` + +### Search Tool Not Found + +```json +{"error": "Search tool not found: tavily-search"} +``` + +**Solution**: Add the search tool to your `proxy_server_config.yaml`: + +```yaml +search_tools: + - search_tool_name: tavily-search + litellm_params: + search_provider: tavily + api_key: os.environ/TAVILY_API_KEY +``` + +## Best Practices + +1. **Use descriptive tool names**: `tavily-marketing` vs `tavily-1` +2. **Empty allowlists for admins**: Grant full access to admin teams +3. **Restrict by role**: Marketing gets marketing tools, engineering gets code search +4. **Monitor costs per team**: Query spend logs regularly +5. **Rotate credentials in tools**: Update environment variables, not team metadata +6. **Start restrictive**: Add tools to allowlists as needed + +## Related + +- [Search API Reference](./search.md) +- [Team Management](./team_budgets.md) +- [Cost Tracking](./cost_tracking.md) diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404/index.html similarity index 100% rename from litellm/proxy/_experimental/out/404.html rename to litellm/proxy/_experimental/out/404/index.html diff --git a/litellm/proxy/_experimental/out/_not-found.html b/litellm/proxy/_experimental/out/_not-found/index.html similarity index 100% rename from litellm/proxy/_experimental/out/_not-found.html rename to litellm/proxy/_experimental/out/_not-found/index.html diff --git a/litellm/proxy/_experimental/out/api-reference.html b/litellm/proxy/_experimental/out/api-reference/index.html similarity index 100% rename from litellm/proxy/_experimental/out/api-reference.html rename to litellm/proxy/_experimental/out/api-reference/index.html diff --git a/litellm/proxy/_experimental/out/chat.html b/litellm/proxy/_experimental/out/chat/index.html similarity index 100% rename from litellm/proxy/_experimental/out/chat.html rename to litellm/proxy/_experimental/out/chat/index.html diff --git a/litellm/proxy/_experimental/out/experimental/api-playground.html b/litellm/proxy/_experimental/out/experimental/api-playground/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/api-playground.html rename to litellm/proxy/_experimental/out/experimental/api-playground/index.html diff --git a/litellm/proxy/_experimental/out/experimental/budgets.html b/litellm/proxy/_experimental/out/experimental/budgets/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/budgets.html rename to litellm/proxy/_experimental/out/experimental/budgets/index.html diff --git a/litellm/proxy/_experimental/out/experimental/caching.html b/litellm/proxy/_experimental/out/experimental/caching/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/caching.html rename to litellm/proxy/_experimental/out/experimental/caching/index.html diff --git a/litellm/proxy/_experimental/out/experimental/claude-code-plugins.html b/litellm/proxy/_experimental/out/experimental/claude-code-plugins/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/claude-code-plugins.html rename to litellm/proxy/_experimental/out/experimental/claude-code-plugins/index.html diff --git a/litellm/proxy/_experimental/out/experimental/old-usage.html b/litellm/proxy/_experimental/out/experimental/old-usage/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/old-usage.html rename to litellm/proxy/_experimental/out/experimental/old-usage/index.html diff --git a/litellm/proxy/_experimental/out/experimental/prompts.html b/litellm/proxy/_experimental/out/experimental/prompts/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/prompts.html rename to litellm/proxy/_experimental/out/experimental/prompts/index.html diff --git a/litellm/proxy/_experimental/out/experimental/tag-management.html b/litellm/proxy/_experimental/out/experimental/tag-management/index.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/tag-management.html rename to litellm/proxy/_experimental/out/experimental/tag-management/index.html diff --git a/litellm/proxy/_experimental/out/guardrails.html b/litellm/proxy/_experimental/out/guardrails/index.html similarity index 100% rename from litellm/proxy/_experimental/out/guardrails.html rename to litellm/proxy/_experimental/out/guardrails/index.html diff --git a/litellm/proxy/_experimental/out/login.html b/litellm/proxy/_experimental/out/login/index.html similarity index 100% rename from litellm/proxy/_experimental/out/login.html rename to litellm/proxy/_experimental/out/login/index.html diff --git a/litellm/proxy/_experimental/out/logs.html b/litellm/proxy/_experimental/out/logs/index.html similarity index 100% rename from litellm/proxy/_experimental/out/logs.html rename to litellm/proxy/_experimental/out/logs/index.html diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback.html b/litellm/proxy/_experimental/out/mcp/oauth/callback/index.html similarity index 100% rename from litellm/proxy/_experimental/out/mcp/oauth/callback.html rename to litellm/proxy/_experimental/out/mcp/oauth/callback/index.html diff --git a/litellm/proxy/_experimental/out/model-hub.html b/litellm/proxy/_experimental/out/model-hub/index.html similarity index 100% rename from litellm/proxy/_experimental/out/model-hub.html rename to litellm/proxy/_experimental/out/model-hub/index.html diff --git a/litellm/proxy/_experimental/out/model_hub.html b/litellm/proxy/_experimental/out/model_hub/index.html similarity index 100% rename from litellm/proxy/_experimental/out/model_hub.html rename to litellm/proxy/_experimental/out/model_hub/index.html diff --git a/litellm/proxy/_experimental/out/model_hub_table.html b/litellm/proxy/_experimental/out/model_hub_table/index.html similarity index 100% rename from litellm/proxy/_experimental/out/model_hub_table.html rename to litellm/proxy/_experimental/out/model_hub_table/index.html diff --git a/litellm/proxy/_experimental/out/models-and-endpoints.html b/litellm/proxy/_experimental/out/models-and-endpoints/index.html similarity index 100% rename from litellm/proxy/_experimental/out/models-and-endpoints.html rename to litellm/proxy/_experimental/out/models-and-endpoints/index.html diff --git a/litellm/proxy/_experimental/out/onboarding.html b/litellm/proxy/_experimental/out/onboarding/index.html similarity index 100% rename from litellm/proxy/_experimental/out/onboarding.html rename to litellm/proxy/_experimental/out/onboarding/index.html diff --git a/litellm/proxy/_experimental/out/organizations.html b/litellm/proxy/_experimental/out/organizations/index.html similarity index 100% rename from litellm/proxy/_experimental/out/organizations.html rename to litellm/proxy/_experimental/out/organizations/index.html diff --git a/litellm/proxy/_experimental/out/playground.html b/litellm/proxy/_experimental/out/playground/index.html similarity index 100% rename from litellm/proxy/_experimental/out/playground.html rename to litellm/proxy/_experimental/out/playground/index.html diff --git a/litellm/proxy/_experimental/out/policies.html b/litellm/proxy/_experimental/out/policies/index.html similarity index 100% rename from litellm/proxy/_experimental/out/policies.html rename to litellm/proxy/_experimental/out/policies/index.html diff --git a/litellm/proxy/_experimental/out/settings/admin-settings.html b/litellm/proxy/_experimental/out/settings/admin-settings/index.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/admin-settings.html rename to litellm/proxy/_experimental/out/settings/admin-settings/index.html diff --git a/litellm/proxy/_experimental/out/settings/logging-and-alerts.html b/litellm/proxy/_experimental/out/settings/logging-and-alerts/index.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/logging-and-alerts.html rename to litellm/proxy/_experimental/out/settings/logging-and-alerts/index.html diff --git a/litellm/proxy/_experimental/out/settings/router-settings.html b/litellm/proxy/_experimental/out/settings/router-settings/index.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/router-settings.html rename to litellm/proxy/_experimental/out/settings/router-settings/index.html diff --git a/litellm/proxy/_experimental/out/settings/ui-theme.html b/litellm/proxy/_experimental/out/settings/ui-theme/index.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/ui-theme.html rename to litellm/proxy/_experimental/out/settings/ui-theme/index.html diff --git a/litellm/proxy/_experimental/out/skills.html b/litellm/proxy/_experimental/out/skills/index.html similarity index 100% rename from litellm/proxy/_experimental/out/skills.html rename to litellm/proxy/_experimental/out/skills/index.html diff --git a/litellm/proxy/_experimental/out/teams.html b/litellm/proxy/_experimental/out/teams/index.html similarity index 100% rename from litellm/proxy/_experimental/out/teams.html rename to litellm/proxy/_experimental/out/teams/index.html diff --git a/litellm/proxy/_experimental/out/test-key.html b/litellm/proxy/_experimental/out/test-key/index.html similarity index 100% rename from litellm/proxy/_experimental/out/test-key.html rename to litellm/proxy/_experimental/out/test-key/index.html diff --git a/litellm/proxy/_experimental/out/tools/mcp-servers.html b/litellm/proxy/_experimental/out/tools/mcp-servers/index.html similarity index 100% rename from litellm/proxy/_experimental/out/tools/mcp-servers.html rename to litellm/proxy/_experimental/out/tools/mcp-servers/index.html diff --git a/litellm/proxy/_experimental/out/tools/vector-stores.html b/litellm/proxy/_experimental/out/tools/vector-stores/index.html similarity index 100% rename from litellm/proxy/_experimental/out/tools/vector-stores.html rename to litellm/proxy/_experimental/out/tools/vector-stores/index.html diff --git a/litellm/proxy/_experimental/out/usage.html b/litellm/proxy/_experimental/out/usage/index.html similarity index 100% rename from litellm/proxy/_experimental/out/usage.html rename to litellm/proxy/_experimental/out/usage/index.html diff --git a/litellm/proxy/_experimental/out/users.html b/litellm/proxy/_experimental/out/users/index.html similarity index 100% rename from litellm/proxy/_experimental/out/users.html rename to litellm/proxy/_experimental/out/users/index.html diff --git a/litellm/proxy/_experimental/out/virtual-keys.html b/litellm/proxy/_experimental/out/virtual-keys/index.html similarity index 100% rename from litellm/proxy/_experimental/out/virtual-keys.html rename to litellm/proxy/_experimental/out/virtual-keys/index.html diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 7b92af92f2..b0c8f54af4 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -1725,7 +1725,7 @@ class TeamBase(LiteLLMPydanticObjectBase): members: list = [] members_with_roles: List[Member] = [] team_member_permissions: Optional[List[str]] = None - metadata: Optional[dict] = None # may include search_provider_config + metadata: Optional[dict] = None tpm_limit: Optional[int] = None rpm_limit: Optional[int] = None @@ -1738,6 +1738,7 @@ class TeamBase(LiteLLMPydanticObjectBase): ) models: list = [] + allowed_search_tools: list = [] # list of search_tool_name values team can access blocked: bool = False router_settings: Optional[dict] = None access_group_ids: Optional[List[str]] = None @@ -1845,13 +1846,6 @@ class UpdateTeamRequest(LiteLLMPydanticObjectBase): ) -class TeamSearchProviderConfigUpdateRequest(LiteLLMPydanticObjectBase): - team_id: str - provider: str - api_key: Optional[str] = None - api_base: Optional[str] = None - - class ResetTeamBudgetRequest(LiteLLMPydanticObjectBase): """ internal type used to reset the budget on a team @@ -2451,6 +2445,7 @@ class LiteLLM_VerificationToken(LiteLLMPydanticObjectBase): max_budget: Optional[float] = None expires: Optional[Union[str, datetime]] = None models: List = [] + allowed_search_tools: List = [] # list of search_tool_name values key can access aliases: Dict = {} config: Dict = {} user_id: Optional[str] = None diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 840f64cfed..7f37783595 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -2962,6 +2962,100 @@ async def can_user_call_model( ) +def _can_object_call_search_tool( + search_tool_name: str, + allowed_search_tools: List[str], + object_type: Literal["key", "team", "project"], +) -> Literal[True]: + """ + Check if an object (key/team/project) can access a specific search tool. + + Similar to _can_object_call_model but for search tools. + + Args: + search_tool_name: The search tool being requested + allowed_search_tools: List of allowed search tool names for this object + object_type: Type of object for error messaging + + Returns: + True if access is allowed + + Raises: + ProxyException if access is denied + """ + # Empty list means all search tools are allowed + if not allowed_search_tools: + return True + + # Check if the search tool is in the allowlist + if search_tool_name in allowed_search_tools: + return True + + # Access denied + raise ProxyException( + message=f"{object_type.capitalize()} not allowed to access search tool: {search_tool_name}. " + f"Allowed search tools: {allowed_search_tools}", + type=ProxyErrorTypes.key_model_access_denied, + param="search_tool_name", + code=status.HTTP_403_FORBIDDEN, + ) + + +async def can_key_call_search_tool( + search_tool_name: str, + valid_token: UserAPIKeyAuth, +) -> Literal[True]: + """ + Check if a key can access a specific search tool. + + Similar to can_key_call_model but for search tools. + + Args: + search_tool_name: The search tool being requested + valid_token: The authenticated key + + Returns: + True if access is allowed + + Raises: + ProxyException if access is denied + """ + return _can_object_call_search_tool( + search_tool_name=search_tool_name, + allowed_search_tools=valid_token.allowed_search_tools or [], + object_type="key", + ) + + +async def can_team_call_search_tool( + search_tool_name: str, + team_object: Optional[LiteLLM_TeamTable], +) -> Literal[True]: + """ + Check if a team can access a specific search tool. + + Similar to can_team_access_model but for search tools. + + Args: + search_tool_name: The search tool being requested + team_object: The team object + + Returns: + True if access is allowed + + Raises: + ProxyException if access is denied + """ + if team_object is None: + return True + + return _can_object_call_search_tool( + search_tool_name=search_tool_name, + allowed_search_tools=team_object.allowed_search_tools or [], + object_type="team", + ) + + async def is_valid_fallback_model( model: str, llm_router: Optional[Router], diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index abd5239c9a..e29b67724c 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -59,7 +59,6 @@ from litellm.proxy._types import ( TeamMemberUpdateResponse, TeamModelAddRequest, TeamModelDeleteRequest, - TeamSearchProviderConfigUpdateRequest, UpdateTeamRequest, UserAPIKeyAuth, ) @@ -1857,108 +1856,6 @@ async def update_team( # noqa: PLR0915 raise handle_exception_on_proxy(e) -@router.post( - "/team/search_provider_config/update", - tags=["team management"], - dependencies=[Depends(user_api_key_auth)], -) -@management_endpoint_wrapper -async def update_team_search_provider_config( - data: TeamSearchProviderConfigUpdateRequest, - user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): - """ - Update per-team search provider credentials in team metadata. - - Stored under: - metadata.search_provider_config..{api_key, api_base} - """ - from litellm.proxy.auth.auth_checks import _cache_team_object - from litellm.proxy.proxy_server import ( - prisma_client, - proxy_logging_obj, - user_api_key_cache, - ) - - if prisma_client is None: - raise HTTPException( - status_code=500, - detail={"error": CommonProxyErrors.db_not_connected_error.value}, - ) - - provider = data.provider.strip().lower() - if provider == "": - raise HTTPException( - status_code=400, detail={"error": "provider cannot be empty"} - ) - - existing_team_row = await prisma_client.db.litellm_teamtable.find_unique( - where={"team_id": data.team_id} - ) - if existing_team_row is None: - raise HTTPException( - status_code=404, - detail={"error": f"Team not found, passed team_id={data.team_id}"}, - ) - - await _verify_team_access( - team_obj=LiteLLM_TeamTable(**existing_team_row.model_dump()), - user_api_key_dict=user_api_key_dict, - ) - - metadata: Dict[str, Any] = {} - if isinstance(existing_team_row.metadata, dict): - metadata = dict(existing_team_row.metadata) - - search_provider_config = metadata.get("search_provider_config") - if not isinstance(search_provider_config, dict): - search_provider_config = {} - - provider_config = search_provider_config.get(provider) - if not isinstance(provider_config, dict): - provider_config = {} - - if data.api_key is not None: - provider_config["api_key"] = data.api_key - if data.api_base is not None: - provider_config["api_base"] = data.api_base - - if provider_config.get("api_key") in (None, "") and provider_config.get( - "api_base" - ) in ( - None, - "", - ): - search_provider_config.pop(provider, None) - else: - search_provider_config[provider] = provider_config - - metadata["search_provider_config"] = search_provider_config - - team_row: Optional[LiteLLM_TeamTable] = ( - await prisma_client.db.litellm_teamtable.update( - where={"team_id": data.team_id}, - data={"metadata": metadata}, - include={"litellm_model_table": True}, # type: ignore - ) - ) - - if team_row is not None and team_row.team_id is not None: - await _cache_team_object( - team_id=team_row.team_id, - team_table=LiteLLM_TeamTableCachedObj(**team_row.model_dump()), - user_api_key_cache=user_api_key_cache, - proxy_logging_obj=proxy_logging_obj, - ) - - return { - "message": "Team search provider configuration updated", - "team_id": data.team_id, - "provider": provider, - "search_provider_config": search_provider_config, - } - - def _set_budget_reset_at(data: UpdateTeamRequest, updated_kv: dict) -> None: """Set budget_reset_at in updated_kv if budget_duration is provided.""" if data.budget_duration is not None: diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 8f07c5afa3..558b4433c2 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -127,6 +127,7 @@ model LiteLLM_TeamTable { soft_budget Float? spend Float @default(0.0) models String[] + allowed_search_tools String[] @default([]) // search_tool_name values team can access max_parallel_requests Int? tpm_limit BigInt? rpm_limit BigInt? @@ -369,6 +370,7 @@ model LiteLLM_VerificationToken { spend Float @default(0.0) expires DateTime? models String[] + allowed_search_tools String[] @default([]) // search_tool_name values key can access aliases Json @default("{}") config Json @default("{}") router_settings Json? @default("{}") diff --git a/litellm/proxy/search_endpoints/endpoints.py b/litellm/proxy/search_endpoints/endpoints.py index ce2949f707..3d79afc7cf 100644 --- a/litellm/proxy/search_endpoints/endpoints.py +++ b/litellm/proxy/search_endpoints/endpoints.py @@ -134,10 +134,41 @@ async def search( if "search_tool_name" in data and data["search_tool_name"]: data["model"] = data["search_tool_name"] + search_tool_name_value = data["search_tool_name"] + + # Authorization check: verify key can access this search tool + from litellm.proxy.auth.auth_checks import ( + can_key_call_search_tool, + can_team_call_search_tool, + get_team_object, + ) + + try: + # Check key-level access + await can_key_call_search_tool( + search_tool_name=search_tool_name_value, + valid_token=user_api_key_dict, + ) + + # Check team-level access if key is associated with a team + if user_api_key_dict.team_id: + team_object = await get_team_object( + team_id=user_api_key_dict.team_id, + user_api_key_cache=None, # Will use internal cache + parent_otel_span=None, + proxy_logging_obj=None, + ) + await can_team_call_search_tool( + search_tool_name=search_tool_name_value, + team_object=team_object, + ) + except Exception as e: + verbose_proxy_logger.error( + f"Search tool authorization failed for {search_tool_name_value}: {str(e)}" + ) + raise if llm_router is not None and hasattr(llm_router, "search_tools"): - search_tool_name_value = data["search_tool_name"] - verbose_proxy_logger.debug( f"Search endpoint - Looking for search_tool_name: {search_tool_name_value}. " f"Available search tools in router: {[tool.get('search_tool_name') for tool in llm_router.search_tools]}. " diff --git a/litellm/router_utils/search_api_router.py b/litellm/router_utils/search_api_router.py index e2e98a6573..4db337a420 100644 --- a/litellm/router_utils/search_api_router.py +++ b/litellm/router_utils/search_api_router.py @@ -56,58 +56,19 @@ class SearchAPIRouter: team_config: Optional[Dict[str, Any]] = None, ) -> Tuple[Optional[str], Optional[str]]: """ - Resolve search provider credentials with precedence: - 1. request metadata.search_provider_config.{provider} - 2. team metadata.search_provider_config.{provider} - 3. default_team_settings.search_provider_config.{provider} - 4. search_tool.litellm_params - 5. env fallback in provider validate_environment() + Resolve search provider credentials from tool configuration ONLY. + + Credentials are stored only in search_tool.litellm_params, never in team/key metadata. + This ensures secrets are not exposed in team/key API responses. + + Args: + tool_litellm_params: Search tool litellm_params with credentials + + Returns: + Tuple of (api_key, api_base) from tool configuration """ - resolved_api_key: Optional[str] = None - resolved_api_base: Optional[str] = None - - request_provider_config = {} - if isinstance(request_metadata, dict): - search_provider_config = request_metadata.get("search_provider_config") - if isinstance(search_provider_config, dict): - request_provider_config = search_provider_config.get( - search_provider, {} - ) - - team_provider_config = {} - if isinstance(team_metadata, dict): - search_provider_config = team_metadata.get("search_provider_config") - if isinstance(search_provider_config, dict): - team_provider_config = search_provider_config.get(search_provider, {}) - - team_settings_provider_config = {} - if isinstance(team_config, dict): - search_provider_config = team_config.get("search_provider_config") - if isinstance(search_provider_config, dict): - team_settings_provider_config = search_provider_config.get( - search_provider, {} - ) - - if isinstance(request_provider_config, dict): - resolved_api_key = request_provider_config.get("api_key") - resolved_api_base = request_provider_config.get("api_base") - - if resolved_api_key is None and isinstance(team_provider_config, dict): - resolved_api_key = team_provider_config.get("api_key") - if resolved_api_base is None and isinstance(team_provider_config, dict): - resolved_api_base = team_provider_config.get("api_base") - - if resolved_api_key is None and isinstance(team_settings_provider_config, dict): - resolved_api_key = team_settings_provider_config.get("api_key") - if resolved_api_base is None and isinstance( - team_settings_provider_config, dict - ): - resolved_api_base = team_settings_provider_config.get("api_base") - - if resolved_api_key is None: - resolved_api_key = tool_litellm_params.get("api_key") - if resolved_api_base is None: - resolved_api_base = tool_litellm_params.get("api_base") + resolved_api_key: Optional[str] = tool_litellm_params.get("api_key") + resolved_api_base: Optional[str] = tool_litellm_params.get("api_base") return resolved_api_key, resolved_api_base diff --git a/proxy_server.log b/proxy_server.log new file mode 100644 index 0000000000..381b28a784 --- /dev/null +++ b/proxy_server.log @@ -0,0 +1,659 @@ +:128: RuntimeWarning: 'litellm.proxy.proxy_cli' found in sys.modules after import of package 'litellm.proxy', but prior to execution of 'litellm.proxy.proxy_cli'; this may result in unpredictable behaviour +2026-04-28 18:52:10,288 - litellm_proxy_extras - INFO - Running prisma migrate deploy +2026-04-28 18:52:13,736 - litellm_proxy_extras - INFO - prisma migrate deploy stdout: Environment variables loaded from ../../.env +Prisma schema loaded from schema.prisma +Datasource "client": PostgreSQL database "litellm", schema "public" at "localhost:5432" + +118 migrations found in prisma/migrations + + +No pending migrations to apply. + +2026-04-28 18:52:13,737 - litellm_proxy_extras - INFO - prisma migrate deploy completed +2026-04-28 18:52:13,737 - litellm_proxy_extras - INFO - No pending migrations — skipping post-migration sanity check +INFO: Started server process [19856] +INFO: Waiting for application startup. +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:803 - litellm.proxy.proxy_server.py::startup() - CHECKING PREMIUM USER - True +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:816 - worker_config: {"model": null, "alias": null, "api_base": null, "api_version": "2025-02-01-preview", "debug": false, "detailed_debug": true, "temperature": null, "max_tokens": null, "request_timeout": null, "max_budget": null, "telemetry": true, "drop_params": false, "add_function_to_prompt": false, "headers": null, "save": false, "config": "proxy_server_config.yaml", "use_queue": false} +LiteLLM Proxy: Using default (v1) migration resolver. If your deployment has seen schema thrashing during rolling deploys, try --use_v2_migration_resolver (safer: avoids the diff-and-force recovery that caused the thrash). + + ██╗ ██╗████████╗███████╗██╗ ██╗ ███╗ ███╗ + ██║ ██║╚══██╔══╝██╔════╝██║ ██║ ████╗ ████║ + ██║ ██║ ██║ █████╗ ██║ ██║ ██╔████╔██║ + ██║ ██║ ██║ ██╔══╝ ██║ ██║ ██║╚██╔╝██║ + ███████╗██║ ██║ ███████╗███████╗███████╗██║ ╚═╝ ██║ + ╚══════╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ + +18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'combined_model_name': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'stripped_model_name': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'combined_stripped_model_name': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'custom_llm_provider': None} +18:52:13 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +18:52:13 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd in litellm.model_cost: 170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd +18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} +18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} +18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'openai/gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} +18:52:13 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=gpt-5.3-codex in litellm.model_cost: gpt-5.3-codex +18:52:13 - LiteLLM Router:DEBUG: router.py:7223 - +Initialized Model List ['gpt-5.3-codex'] +18:52:13 - LiteLLM Router:INFO: router.py:812 - Routing strategy: simple-shuffle +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:3915 - Policy engine: no policies in config, skipping +18:52:13 - LiteLLM Proxy:DEBUG: utils.py:2460 - Creating Prisma Client.. +18:52:13 - LiteLLM Proxy:DEBUG: utils.py:2527 - Success - Created Prisma Client +18:52:13 - LiteLLM Proxy:DEBUG: utils.py:3745 - PrismaClient: connect() called Attempting to Connect to DB +18:52:13 - LiteLLM Proxy:DEBUG: utils.py:3749 - PrismaClient: DB not connected, Attempting to Connect to DB +query-engine ac9d7041ed77bcc8a8dbd2ab6616b39013829574 +18:52:13 - LiteLLM Proxy:DEBUG: prisma_client.py:247 - IAM token auth not enabled, skipping token refresh task +18:52:13 - LiteLLM Proxy:INFO: utils.py:4307 - Started Prisma DB health watchdog (interval=30s, reconnect_cooldown=15s, probe_timeout=5.0s, reconnect_timeout=30.0s) +18:52:13 - LiteLLM Proxy:INFO: utils.py:4062 - Found prisma-query-engine at PID 20355. +18:52:13 - LiteLLM Proxy:INFO: utils.py:4066 - Watching engine PID 20355 via waitpid thread. +18:52:13 - LiteLLM:DEBUG: logging_callback_manager.py:336 - Custom logger of type SkillsInjectionHook, key: SkillsInjectionHook-max_iterations=10-sandbox_timeout=120-message_logging=True-turn_off_message_logging=False already exists in [, , , , , , ], not adding again.. +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:895 - About to initialize semantic tool filter +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:898 - litellm_settings keys = [] +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:6220 - Semantic tool filter not configured or not enabled, skipping initialization +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:905 - After semantic tool filter initialization +18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:919 - prisma_client: +18:52:13 - LiteLLM Proxy:INFO: proxy_server.py:6467 - Tag spend update job scheduled at 25s interval (2.3x main job interval) +18:52:13 - LiteLLM Proxy:DEBUG: hanging_request_check.py:148 - Checking for hanging requests.... +18:52:13 - LiteLLM Proxy:INFO: utils.py:5083 - Starting spend logs queue monitor (threshold: 100, poll_interval: 2.0s) +18:52:14 - LiteLLM Proxy:INFO: utils.py:2634 - All necessary views exist! +18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:874 - Password migration: No plaintext passwords found +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:4256 - len new_models: 2 +18:52:14 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': '88d4dde8-817a-4c20-9bec-442961096d25', 'combined_model_name': '88d4dde8-817a-4c20-9bec-442961096d25', 'stripped_model_name': '88d4dde8-817a-4c20-9bec-442961096d25', 'combined_stripped_model_name': '88d4dde8-817a-4c20-9bec-442961096d25', 'custom_llm_provider': None} +18:52:14 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +18:52:14 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=88d4dde8-817a-4c20-9bec-442961096d25 in litellm.model_cost: 88d4dde8-817a-4c20-9bec-442961096d25 +18:52:14 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'combined_model_name': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'stripped_model_name': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'combined_stripped_model_name': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'custom_llm_provider': None} +18:52:14 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +18:52:14 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=2ab3179b-62e9-4720-9ba7-0a2f12536cfa in litellm.model_cost: 2ab3179b-62e9-4720-9ba7-0a2f12536cfa +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5461 - guardrails from the DB [] +18:52:14 - LiteLLM Proxy:INFO: policy_registry.py:577 - Synced 0 production policies and 0 draft/published (by ID) from DB to in-memory registry +18:52:14 - LiteLLM Proxy:INFO: attachment_registry.py:481 - Synced 0 attachments from DB to in-memory registry +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5497 - Successfully synced policies and attachments from DB +18:52:14 - LiteLLM:DEBUG: mcp_server_manager.py:2644 - Loading MCP servers from database into registry... +18:52:14 - LiteLLM:INFO: mcp_server_manager.py:2664 - Found 1 MCP servers in database +18:52:14 - LiteLLM:DEBUG: mcp_server_manager.py:2688 - Building server from DB: 28a195c6-0224-4765-af9b-46f7a7f65ccb (deepwiki) +18:52:14 - LiteLLM:DEBUG: mcp_server_manager.py:2697 - MCP registry refreshed (1 servers in registry) +18:52:14 - LiteLLM Proxy:DEBUG: pass_through_endpoints.py:2409 - initializing pass through endpoints +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.weave +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.litellm_agent +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.dotprompt +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.dotprompt: ['dotprompt'] +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.gitlab +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.gitlab: ['gitlab'] +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.azure_sentinel +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.arize +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.arize: ['arize_phoenix'] +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.agentops +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.compression_interception +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.focus +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.prometheus_helpers +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.generic_prompt_management +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.generic_prompt_management: ['generic_prompt_management'] +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.levo +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.websearch_interception +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.deepeval +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.bitbucket +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.bitbucket: ['bitbucket'] +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.vantage +18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:76 - Discovered 5 prompt initializers: ['dotprompt', 'gitlab', 'arize_phoenix', 'generic_prompt_management', 'bitbucket'] +18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:5640 - Loading 0 search tool(s) from database into router +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5660 - No search tools found in database, keeping config-loaded search tools (if any) +18:52:14 - LiteLLM Proxy:INFO: tool_registry_writer.py:329 - ToolPolicyRegistry: synced 17 tool policies and 1 object permissions from DB +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5517 - Successfully synced tool policy from DB +18:52:14 - LiteLLM:DEBUG: focus_logger.py:167 - No Focus export logger registered; skipping scheduler +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6759 - key_rotation_enabled: False +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6793 - Key rotation disabled (set LITELLM_KEY_ROTATION_ENABLED=true to enable) +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6824 - expired_ui_session_key_cleanup_enabled: False +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6869 - Expired UI session key cleanup disabled (set LITELLM_EXPIRED_UI_SESSION_KEY_CLEANUP_ENABLED=true to enable) +18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:6622 - Batch cost check job scheduled successfully +18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:6653 - Responses cost check job scheduled successfully +18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:6672 - APScheduler started with memory leak prevention settings: removed jitter, increased intervals, misfire_grace_time=3600 +18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:7036 - LiteLLM: Pyroscope profiling is disabled (set LITELLM_ENABLE_PYROSCOPE=true to enable). +18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:755 - SESSION REUSE: Created shared aiohttp session for connection pooling (ID: 6165409104, limit=1000, limit_per_host=500) +INFO: Application startup complete. +INFO: Uvicorn running on http://0.0.0.0:4000 (Press CTRL+C to quit) +18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. +18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e +18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. +18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e +18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list +18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. +18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e +18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/key/list +18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. +18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e + +#------------------------------------------------------------# +# # +# 'The thing I wish you improved is...' # +# https://github.com/BerriAI/litellm/issues/new # +# # +#------------------------------------------------------------# + + Thank you for using LiteLLM! - Krrish & Ishaan + + + +Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new + + +LiteLLM: Proxy initialized with Config, Set models: + gpt-5.3-codex +INFO: 127.0.0.1:65291 - "GET /project/list HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:65293 - "HEAD / HTTP/1.1" 200 OK +INFO: 127.0.0.1:65293 - "GET /__next._tree.txt?_rsc=1r34m HTTP/1.1" 404 Not Found +18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.984645+00:00 +18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.987707+00:00 +18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.988286+00:00 +18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.988897+00:00 +18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:22 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:22 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:22 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4526 - Entering list_keys function +18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4950 - Filter conditions: {'OR': [{'team_id': None}, {'team_id': {'not': 'litellm-dashboard'}}]} +18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5020 - Pagination: skip=0, take=50 +18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65283 - "GET /organization/list HTTP/1.1" 200 OK +18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5062 - Fetched 4 keys +18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5074 - Total count of keys: 4 +INFO: 127.0.0.1:65285 - "GET /team/list HTTP/1.1" 200 OK +18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4604 - Successfully prepared response +INFO: 127.0.0.1:65290 - "GET /key/list?page=1&size=50&sort_by=created_at&sort_order=desc&expand=user&return_full_object=true&include_team_keys=true&include_created_by_keys=true HTTP/1.1" 200 OK +INFO: 127.0.0.1:65288 - "GET /tag/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65290 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:24 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:52:24 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 +INFO: 127.0.0.1:65290 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.414705+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.416982+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/models +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.426995+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v2/user/info +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.430882+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/access_group +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.434375+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.437819+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +INFO: 127.0.0.1:65326 - "GET /v2/user/info HTTP/1.1" 200 OK +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/access_groups +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.460826+00:00 +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65324 - "GET /models?include_model_access_groups=True&return_wildcard_routes=True&scope=expand HTTP/1.1" 200 OK +18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/toolset +18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.463333+00:00 +INFO: 127.0.0.1:65290 - "GET /organization/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65328 - "GET /v1/access_group HTTP/1.1" 200 OK +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers +INFO: 127.0.0.1:65329 - "GET /v1/mcp/server HTTP/1.1" 200 OK +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +INFO: 127.0.0.1:65321 - "GET /team/list HTTP/1.1" 200 OK +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:28 - LiteLLM Proxy:WARNING: toolset_db.py:60 - litellm.proxy._experimental.mcp_server.toolset_db::list_mcp_toolsets - 'Prisma' object has no attribute 'litellm_mcptoolsettable' +INFO: 127.0.0.1:65324 - "GET /v1/mcp/toolset HTTP/1.1" 200 OK +INFO: 127.0.0.1:65326 - "GET /v1/mcp/access_groups HTTP/1.1" 200 OK +18:52:30 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:52:30 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:30.307066+00:00 +18:52:30 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:52:30 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:30.310005+00:00 +INFO: 127.0.0.1:65321 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:30 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list +18:52:30 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:30.314509+00:00 +18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:30 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:30 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:30 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65326 - "GET /organization/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65324 - "GET /team/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65329 - "GET /tag/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65329 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:34 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:52:34 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:34.018157+00:00 +18:52:34 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:52:34 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:34.038105+00:00 +18:52:34 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list +18:52:34 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:34.045120+00:00 +INFO: 127.0.0.1:65321 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:34 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:34 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:34 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65329 - "GET /organization/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65324 - "GET /team/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65326 - "GET /tag/list HTTP/1.1" 200 OK +18:52:35 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:52:35 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 +INFO: 127.0.0.1:65326 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:39 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:4256 - len new_models: 2 +18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5461 - guardrails from the DB [] +18:52:44 - LiteLLM Proxy:INFO: policy_registry.py:577 - Synced 0 production policies and 0 draft/published (by ID) from DB to in-memory registry +18:52:44 - LiteLLM Proxy:INFO: attachment_registry.py:481 - Synced 0 attachments from DB to in-memory registry +18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5497 - Successfully synced policies and attachments from DB +18:52:44 - LiteLLM:DEBUG: mcp_server_manager.py:2644 - Loading MCP servers from database into registry... +18:52:44 - LiteLLM:INFO: mcp_server_manager.py:2664 - Found 1 MCP servers in database +18:52:44 - LiteLLM:DEBUG: mcp_server_manager.py:2697 - MCP registry refreshed (1 servers in registry) +18:52:44 - LiteLLM Proxy:DEBUG: pass_through_endpoints.py:2409 - initializing pass through endpoints +18:52:44 - LiteLLM Proxy:INFO: proxy_server.py:5640 - Loading 0 search tool(s) from database into router +18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5660 - No search tools found in database, keeping config-loaded search tools (if any) +18:52:44 - LiteLLM Proxy:INFO: tool_registry_writer.py:329 - ToolPolicyRegistry: synced 17 tool policies and 1 object permissions from DB +18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5517 - Successfully synced tool policy from DB +18:52:46 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:52:46 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 +18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.075515+00:00 +18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.133027+00:00 +INFO: 127.0.0.1:65389 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list +18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.284965+00:00 +18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/key/list +18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.295543+00:00 +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4526 - Entering list_keys function +18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4950 - Filter conditions: {'OR': [{'team_id': None}, {'team_id': {'not': 'litellm-dashboard'}}]} +18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5020 - Pagination: skip=0, take=50 +18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65385 - "GET /organization/list HTTP/1.1" 200 OK +18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5062 - Fetched 4 keys +INFO: 127.0.0.1:65392 - "GET /tag/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65387 - "GET /team/list HTTP/1.1" 200 OK +18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5074 - Total count of keys: 4 +18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4604 - Successfully prepared response +INFO: 127.0.0.1:65393 - "GET /key/list?page=1&size=50&sort_by=created_at&sort_order=desc&expand=user&return_full_object=true&include_team_keys=true&include_created_by_keys=true HTTP/1.1" 200 OK +INFO: 127.0.0.1:65393 - "GET /project/list HTTP/1.1" 404 Not Found +18:52:58 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:52:58 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 +18:53:04 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:05 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/health/readiness +18:53:05 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:53:05 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:05 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:05.652862+00:00 +18:53:06 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:53:06 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:06.042541+00:00 +18:53:06 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list +18:53:06 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:06.085990+00:00 +INFO: 127.0.0.1:65443 - "GET /project/list HTTP/1.1" 404 Not Found +18:53:06 - LiteLLM:DEBUG: http_handler.py:840 - Using AiohttpTransport... +INFO: 127.0.0.1:65436 - "GET /health/readiness HTTP/1.1" 200 OK +18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:06 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:06 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:06 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65438 - "GET /organization/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65442 - "GET /tag/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65440 - "GET /team/list HTTP/1.1" 200 OK +18:53:08 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:53:09 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.447694+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/toolset +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.480143+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/get/mcp_semantic_filter_settings +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.511525+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/model_group/info +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.521031+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/config/list +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.525741+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/network/client-ip +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.533092+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65461 - "GET /v1/mcp/network/client-ip HTTP/1.1" 200 OK +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:09 - LiteLLM Proxy:DEBUG: model_checks.py:131 - ALL KEY MODELS - 0 +18:53:09 - LiteLLM Proxy:DEBUG: model_checks.py:166 - ALL TEAM MODELS - 0 +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'openai/gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} +18:53:09 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} +18:53:09 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gemini-3.1-flash-image-preview', 'combined_model_name': 'vertex_ai/gemini-3.1-flash-image-preview', 'stripped_model_name': 'gemini-3.1-flash-image-preview', 'combined_stripped_model_name': 'vertex_ai/gemini-3.1-flash-image-preview', 'custom_llm_provider': 'vertex_ai'} +18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gemini-3.1-flash-image-preview', 'combined_model_name': 'gemini-3.1-flash-image-preview', 'stripped_model_name': 'gemini-3.1-flash-image-preview', 'combined_stripped_model_name': 'gemini-3.1-flash-image-preview', 'custom_llm_provider': 'vertex_ai'} +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server/submissions +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.669506+00:00 +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:09 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers +INFO: 127.0.0.1:65440 - "GET /v1/mcp/server HTTP/1.1" 200 OK +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:09 - LiteLLM Proxy:WARNING: toolset_db.py:60 - litellm.proxy._experimental.mcp_server.toolset_db::list_mcp_toolsets - 'Prisma' object has no attribute 'litellm_mcptoolsettable' +INFO: 127.0.0.1:65442 - "GET /v1/mcp/toolset HTTP/1.1" 200 OK +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server/health +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.692446+00:00 +INFO: 127.0.0.1:65442 - "HEAD / HTTP/1.1" 200 OK +INFO: 127.0.0.1:65436 - "GET /model_group/info HTTP/1.1" 200 OK +INFO: 127.0.0.1:65443 - "GET /config/list?config_type=general_settings HTTP/1.1" 200 OK +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/config/list +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.731878+00:00 +INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/github.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/slack.svg HTTP/1.1" 304 Not Modified +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/notion.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/linear.svg HTTP/1.1" 304 Not Modified +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/jira.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65438 - "GET /get/mcp_semantic_filter_settings HTTP/1.1" 200 OK +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/figma.svg HTTP/1.1" 304 Not Modified +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:09 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers +18:53:09 - LiteLLM:DEBUG: client.py:273 - litellm headers for streamable_http_client: {} +18:53:09 - LiteLLM:DEBUG: client.py:402 - MCP client using SSL configuration: SSLContext +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/gmail.svg HTTP/1.1" 304 Not Modified +18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/model_group/info +18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.989425+00:00 +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/stripe.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/google_drive.svg HTTP/1.1" 304 Not Modified +18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/shopify.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/salesforce.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65442 - "GET /config/list?config_type=general_settings HTTP/1.1" 200 OK +INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/hubspot.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/twilio.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65461 - "GET /v1/mcp/server/submissions HTTP/1.1" 200 OK +INFO: 127.0.0.1:65442 - "GET /ui/assets/logos/cloudflare.svg HTTP/1.1" 304 Not Modified +18:53:10 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/postgresql.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/sentry.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65461 - "GET /ui/assets/logos/snowflake.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65442 - "GET /ui/assets/logos/zapier.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65461 - "GET /__next._tree.txt?_rsc=1r34m HTTP/1.1" 404 Not Found +18:53:10 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:10 - LiteLLM Proxy:DEBUG: model_checks.py:131 - ALL KEY MODELS - 0 +18:53:10 - LiteLLM Proxy:DEBUG: model_checks.py:166 - ALL TEAM MODELS - 0 +18:53:10 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} +18:53:10 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +18:53:10 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} +18:53:10 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json +INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/gitlab.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/google.svg HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:65436 - "GET /model_group/info HTTP/1.1" 200 OK +INFO: 127.0.0.1:65440 - "GET /v1/mcp/server/health HTTP/1.1" 200 OK +INFO: 127.0.0.1:65440 - "GET / HTTP/1.1" 200 OK +18:53:17 - LiteLLM Proxy:DEBUG: custom_openapi_spec.py:311 - Successfully added ProxyChatCompletionRequest schema to OpenAPI spec +18:53:17 - LiteLLM Proxy:DEBUG: custom_openapi_spec.py:311 - Successfully added EmbeddingRequest schema to OpenAPI spec +18:53:17 - LiteLLM Proxy:DEBUG: custom_openapi_spec.py:315 - Could not get schema for ResponsesAPIRequestParams +INFO: 127.0.0.1:65440 - "GET /openapi.json HTTP/1.1" 200 OK +18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list +18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.073159+00:00 +18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list +18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.085893+00:00 +18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server +18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.089077+00:00 +18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server/health +18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.093754+00:00 +18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/toolset +18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.096736+00:00 +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers +INFO: 127.0.0.1:65438 - "GET /v1/mcp/server HTTP/1.1" 200 OK +18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) +18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:4256 - len new_models: 2 +18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers +18:53:17 - LiteLLM:DEBUG: client.py:273 - litellm headers for streamable_http_client: {} +18:53:17 - LiteLLM:DEBUG: client.py:402 - MCP client using SSL configuration: SSLContext +18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +18:53:17 - LiteLLM Proxy:WARNING: toolset_db.py:60 - litellm.proxy._experimental.mcp_server.toolset_db::list_mcp_toolsets - 'Prisma' object has no attribute 'litellm_mcptoolsettable' +INFO: 127.0.0.1:65442 - "GET /v1/mcp/toolset HTTP/1.1" 200 OK +18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check +INFO: 127.0.0.1:65436 - "GET /organization/list HTTP/1.1" 200 OK +INFO: 127.0.0.1:65443 - "GET /team/list HTTP/1.1" 200 OK +18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5461 - guardrails from the DB [] +18:53:17 - LiteLLM Proxy:INFO: policy_registry.py:577 - Synced 0 production policies and 0 draft/published (by ID) from DB to in-memory registry +18:53:17 - LiteLLM Proxy:INFO: attachment_registry.py:481 - Synced 0 attachments from DB to in-memory registry +18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5497 - Successfully synced policies and attachments from DB +18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:2644 - Loading MCP servers from database into registry... +18:53:17 - LiteLLM:INFO: mcp_server_manager.py:2664 - Found 1 MCP servers in database +18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:2697 - MCP registry refreshed (1 servers in registry) +18:53:17 - LiteLLM Proxy:DEBUG: pass_through_endpoints.py:2409 - initializing pass through endpoints +18:53:17 - LiteLLM Proxy:INFO: proxy_server.py:5640 - Loading 0 search tool(s) from database into router +18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5660 - No search tools found in database, keeping config-loaded search tools (if any) +18:53:17 - LiteLLM Proxy:INFO: tool_registry_writer.py:329 - ToolPolicyRegistry: synced 17 tool policies and 1 object permissions from DB +18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5517 - Successfully synced tool policy from DB +INFO: 127.0.0.1:65461 - "GET /v1/mcp/server/health HTTP/1.1" 200 OK +18:53:19 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:53:20 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 +18:53:29 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update +18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update +18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update +18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update +18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 +18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update +18:53:31 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 diff --git a/proxy_server_config.yaml b/proxy_server_config.yaml index 5d3d810926..5a34fd4745 100644 --- a/proxy_server_config.yaml +++ b/proxy_server_config.yaml @@ -1,231 +1,7 @@ model_list: - - model_name: gpt-3.5-turbo-end-user-test + # Gemini 2.5 Flash Native Audio (Latest - recommended) + - model_name: gpt-5.3-codex litellm_params: - model: gpt-3.5-turbo - region_name: "eu" - model_info: - id: "1" - - model_name: gpt-3.5-turbo-end-user-test - litellm_params: - model: openai/gpt-4.1-mini - api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault - - model_name: gpt-3.5-turbo - litellm_params: - model: openai/gpt-4.1-mini - api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault - - model_name: gpt-3.5-turbo-large - litellm_params: - model: "gpt-3.5-turbo-1106" + model: openai/gpt-5.3-codex api_key: os.environ/OPENAI_API_KEY - rpm: 480 - timeout: 300 - stream_timeout: 60 - - model_name: gpt-4 - litellm_params: - model: openai/gpt-4.1-mini - api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault - rpm: 480 - timeout: 300 - stream_timeout: 60 - - model_name: sagemaker-completion-model - litellm_params: - model: sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4 - input_cost_per_second: 0.000420 - - model_name: text-embedding-ada-002 - litellm_params: - model: openai/text-embedding-ada-002 - api_key: os.environ/OPENAI_API_KEY - model_info: - mode: embedding - base_model: text-embedding-ada-002 - - model_name: dall-e-2 # some tests use dall-e-2 which is now deprecated, alias to dall-e-3 - litellm_params: - model: openai/dall-e-3 - - model_name: openai-dall-e-3 - litellm_params: - model: dall-e-3 - - model_name: fake-openai-endpoint - litellm_params: - model: openai/gpt-3.5-turbo - api_key: fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - - model_name: fake-openai-endpoint-2 - litellm_params: - model: openai/my-fake-model - api_key: my-fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - stream_timeout: 0.001 - rpm: 1 - - model_name: fake-openai-endpoint-3 - litellm_params: - model: openai/my-fake-model - api_key: my-fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - stream_timeout: 0.001 - rpm: 1000 - - model_name: fake-openai-endpoint-4 - litellm_params: - model: openai/my-fake-model - api_key: my-fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - num_retries: 50 - - model_name: fake-openai-endpoint-3 - litellm_params: - model: openai/my-fake-model-2 - api_key: my-fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - stream_timeout: 0.001 - rpm: 1000 - - model_name: bad-model - litellm_params: - model: openai/bad-model - api_key: os.environ/OPENAI_API_KEY - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - mock_timeout: True - timeout: 60 - rpm: 1000 - model_info: - health_check_timeout: 1 - - model_name: good-model - litellm_params: - model: openai/bad-model - api_key: os.environ/OPENAI_API_KEY - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - rpm: 1000 - model_info: - health_check_timeout: 1 - - model_name: "*" - litellm_params: - model: openai/* - api_key: os.environ/OPENAI_API_KEY - - model_name: realtime-v1 - litellm_params: - model: azure/gpt-realtime-20250828-standard - api_version: "2025-08-28" - realtime_protocol: GA # Possible values: "GA"/ "v1", "beta" - - - model_name: realtime-beta - litellm_params: - model: azure/gpt-realtime-20250828-standard - api_version: 2025-04-01-preview - - - # provider specific wildcard routing - - model_name: "anthropic/*" - litellm_params: - model: "anthropic/*" - api_key: os.environ/ANTHROPIC_API_KEY - - model_name: "bedrock/*" - litellm_params: - model: "bedrock/*" - - model_name: "groq/*" - litellm_params: - model: "groq/*" - api_key: os.environ/GROQ_API_KEY - - model_name: mistral-embed - litellm_params: - model: mistral/mistral-embed - - model_name: gpt-instruct # [PROD TEST] - tests if `/health` automatically infers this to be a text completion model - litellm_params: - model: text-completion-openai/gpt-3.5-turbo-instruct - - model_name: fake-openai-endpoint-5 - litellm_params: - model: openai/my-fake-model - api_key: my-fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.app/ - timeout: 1 - - model_name: badly-configured-openai-endpoint - litellm_params: - model: openai/my-fake-model - api_key: my-fake-key - api_base: https://exampleopenaiendpoint-production.up.railway.appxxxx/ - - model_name: gemini-1.5-flash - litellm_params: - model: gemini/gemini-1.5-flash - api_key: os.environ/GOOGLE_API_KEY - - model_name: gpt-4o - litellm_params: - model: gpt-4o - api_key: os.environ/OPENAI_API_KEY - - -litellm_settings: - # set_verbose: True # Uncomment this if you want to see verbose logs; not recommended in production - drop_params: True - success_callback: ["prometheus"] - # max_budget: 100 - # budget_duration: 30d - num_retries: 5 - request_timeout: 600 - telemetry: False - context_window_fallbacks: [{"gpt-3.5-turbo": ["gpt-3.5-turbo-large"]}] - default_team_settings: - - team_id: team-1 - success_callback: ["langfuse"] - failure_callback: ["langfuse"] - langfuse_public_key: os.environ/LANGFUSE_PROJECT1_PUBLIC # Project 1 - langfuse_secret: os.environ/LANGFUSE_PROJECT1_SECRET # Project 1 - - team_id: team-2 - success_callback: ["langfuse"] - failure_callback: ["langfuse"] - langfuse_public_key: os.environ/LANGFUSE_PROJECT2_PUBLIC # Project 2 - langfuse_secret: os.environ/LANGFUSE_PROJECT2_SECRET # Project 2 - langfuse_host: https://us.cloud.langfuse.com - # cache: true # [OPTIONAL] use for caching responses - # enable_caching_on_provider_specific_optional_params: True # Include provider-specific params in cache keys - # cache_params: # And for shared health check - # type: redis - # host: localhost - # port: 6379 - -# For /fine_tuning/jobs endpoints -finetune_settings: - - custom_llm_provider: azure - api_base: os.environ/AZURE_API_BASE - api_key: os.environ/AZURE_API_KEY - api_version: "2023-03-15-preview" - - custom_llm_provider: openai - api_key: os.environ/OPENAI_API_KEY - -# for /files endpoints -files_settings: - - custom_llm_provider: azure - api_base: os.environ/AZURE_API_BASE - api_key: os.environ/AZURE_API_KEY - api_version: "2023-03-15-preview" - - custom_llm_provider: openai - api_key: os.environ/OPENAI_API_KEY - -router_settings: - routing_strategy: usage-based-routing-v2 - redis_host: os.environ/REDIS_HOST - redis_password: os.environ/REDIS_PASSWORD - redis_port: os.environ/REDIS_PORT - enable_pre_call_checks: true - model_group_alias: {"my-special-fake-model-alias-name": "fake-openai-endpoint-3"} - -general_settings: - master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys - store_model_in_db: True - proxy_budget_rescheduler_min_time: 60 - proxy_budget_rescheduler_max_time: 64 - proxy_batch_write_at: 1 - database_connection_pool_limit: 10 - # background_health_checks: true - # use_shared_health_check: true - # health_check_interval: 30 - # database_url: "postgresql://:@:/" # [OPTIONAL] use for token-based auth to proxy - - pass_through_endpoints: - - path: "/v1/rerank" # route you want to add to LiteLLM Proxy Server - target: "https://api.cohere.com/v1/rerank" # URL this route should forward requests to - headers: # headers to forward to this URL - content-type: application/json # (Optional) Extra Headers to pass to this endpoint - accept: application/json - forward_headers: True - -# environment_variables: - # settings for using redis caching - # REDIS_HOST: redis-16337.c322.us-east-1-2.ec2.cloud.redislabs.com - # REDIS_PORT: "16337" - # REDIS_PASSWORD: \ No newline at end of file + \ No newline at end of file diff --git a/tests/test_proxy_search_tool_auth.py b/tests/test_proxy_search_tool_auth.py new file mode 100644 index 0000000000..502bc655ce --- /dev/null +++ b/tests/test_proxy_search_tool_auth.py @@ -0,0 +1,212 @@ +""" +Test search tool authorization - verify model-like access control for search tools. + +Tests that: +1. Keys can only access search tools in their allowed_search_tools list +2. Teams can only access search tools in their allowed_search_tools list +3. Empty allowlists grant access to all search tools +4. Credentials are never exposed in team/key metadata +""" + +import pytest +from unittest.mock import MagicMock, patch +from fastapi import HTTPException + +# Import types and functions to test +from litellm.proxy._types import LiteLLM_TeamTable, UserAPIKeyAuth +from litellm.proxy.auth.auth_checks import ( + can_key_call_search_tool, + can_team_call_search_tool, +) + + +@pytest.mark.asyncio +async def test_key_can_access_allowed_search_tool(): + """Test that a key can access a search tool in its allowlist.""" + # Create a mock key with allowed_search_tools + mock_key = UserAPIKeyAuth( + token="sk-test-key", + models=["gpt-4"], + allowed_search_tools=["tavily-search", "perplexity-search"], + ) + + # Should succeed - tool is in allowlist + result = await can_key_call_search_tool( + search_tool_name="tavily-search", + valid_token=mock_key, + ) + assert result is True + + +@pytest.mark.asyncio +async def test_key_denied_non_allowed_search_tool(): + """Test that a key is denied access to a search tool not in its allowlist.""" + mock_key = UserAPIKeyAuth( + token="sk-test-key", + models=["gpt-4"], + allowed_search_tools=["tavily-search"], # Only tavily allowed + ) + + # Should raise exception - brave-search not in allowlist + with pytest.raises(Exception) as exc_info: + await can_key_call_search_tool( + search_tool_name="brave-search", + valid_token=mock_key, + ) + assert "not allowed to access search tool" in str(exc_info.value) + assert "brave-search" in str(exc_info.value) + + +@pytest.mark.asyncio +async def test_key_empty_allowlist_grants_all_access(): + """Test that an empty allowlist grants access to all search tools.""" + mock_key = UserAPIKeyAuth( + token="sk-test-key", + models=["gpt-4"], + allowed_search_tools=[], # Empty = all allowed + ) + + # Should succeed - empty list allows all + result = await can_key_call_search_tool( + search_tool_name="any-search-tool", + valid_token=mock_key, + ) + assert result is True + + +@pytest.mark.asyncio +async def test_team_can_access_allowed_search_tool(): + """Test that a team can access a search tool in its allowlist.""" + mock_team = LiteLLM_TeamTable( + team_id="team-123", + team_alias="Marketing Team", + models=["gpt-4"], + allowed_search_tools=["tavily-search", "exa-search"], + ) + + # Should succeed - tool is in allowlist + result = await can_team_call_search_tool( + search_tool_name="tavily-search", + team_object=mock_team, + ) + assert result is True + + +@pytest.mark.asyncio +async def test_team_denied_non_allowed_search_tool(): + """Test that a team is denied access to a search tool not in its allowlist.""" + mock_team = LiteLLM_TeamTable( + team_id="team-123", + team_alias="Engineering Team", + models=["gpt-4"], + allowed_search_tools=["perplexity-search"], # Only perplexity allowed + ) + + # Should raise exception - tavily-search not in allowlist + with pytest.raises(Exception) as exc_info: + await can_team_call_search_tool( + search_tool_name="tavily-search", + team_object=mock_team, + ) + assert "not allowed to access search tool" in str(exc_info.value) + assert "tavily-search" in str(exc_info.value) + + +@pytest.mark.asyncio +async def test_team_empty_allowlist_grants_all_access(): + """Test that an empty team allowlist grants access to all search tools.""" + mock_team = LiteLLM_TeamTable( + team_id="team-123", + team_alias="Admin Team", + models=["gpt-4"], + allowed_search_tools=[], # Empty = all allowed + ) + + # Should succeed - empty list allows all + result = await can_team_call_search_tool( + search_tool_name="any-search-tool", + team_object=mock_team, + ) + assert result is True + + +@pytest.mark.asyncio +async def test_team_none_allowed_search_tools(): + """Test that None for allowed_search_tools (not set) grants access to all.""" + mock_team = LiteLLM_TeamTable( + team_id="team-123", + team_alias="Legacy Team", + models=["gpt-4"], + allowed_search_tools=None, # Not set = all allowed + ) + + # Should succeed - None allows all + result = await can_team_call_search_tool( + search_tool_name="any-search-tool", + team_object=mock_team, + ) + assert result is True + + +def test_credentials_not_in_team_metadata(): + """Verify that search provider credentials are never stored in team metadata.""" + mock_team = LiteLLM_TeamTable( + team_id="team-123", + team_alias="Test Team", + models=["gpt-4"], + allowed_search_tools=["tavily-search"], + metadata={"custom_field": "value"}, # No search_provider_config + ) + + # Verify metadata does not contain search_provider_config + assert mock_team.metadata is not None + assert "search_provider_config" not in mock_team.metadata + assert "api_key" not in str(mock_team.metadata) + + +def test_credentials_not_in_key_metadata(): + """Verify that search provider credentials are never stored in key metadata.""" + mock_key = UserAPIKeyAuth( + token="sk-test-key", + models=["gpt-4"], + allowed_search_tools=["tavily-search"], + metadata={"user_info": "test"}, # No search_provider_config + ) + + # Verify metadata does not contain search_provider_config + assert mock_key.metadata is not None + assert "search_provider_config" not in mock_key.metadata + assert "api_key" not in str(mock_key.metadata) + + +@pytest.mark.asyncio +async def test_both_key_and_team_checks_required(): + """Test that both key-level and team-level checks are enforced.""" + # Key has access to tool + mock_key = UserAPIKeyAuth( + token="sk-test-key", + models=["gpt-4"], + allowed_search_tools=["tavily-search"], + ) + + # Team does NOT have access to tool + mock_team = LiteLLM_TeamTable( + team_id="team-123", + team_alias="Restricted Team", + models=["gpt-4"], + allowed_search_tools=["perplexity-search"], # Different tool + ) + + # Key check passes + await can_key_call_search_tool( + search_tool_name="tavily-search", + valid_token=mock_key, + ) + + # Team check fails + with pytest.raises(Exception) as exc_info: + await can_team_call_search_tool( + search_tool_name="tavily-search", + team_object=mock_team, + ) + assert "not allowed to access search tool" in str(exc_info.value) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx index 1859970091..2e14897792 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx @@ -15,7 +15,15 @@ import ModelAliasManager from "@/components/common_components/ModelAliasManager" import React, { useEffect, useState } from "react"; import { useQueryClient } from "@tanstack/react-query"; import NotificationsManager from "@/components/molecules/notifications_manager"; -import { fetchMCPAccessGroups, getGuardrailsList, getPoliciesList, Organization, Team, teamCreateCall } from "@/components/networking"; +import { + fetchMCPAccessGroups, + fetchSearchTools, + getGuardrailsList, + getPoliciesList, + Organization, + Team, + teamCreateCall, +} from "@/components/networking"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { organizationKeys } from "@/app/(dashboard)/hooks/organizations/useOrganizations"; import MCPToolPermissions from "@/components/mcp_server_management/MCPToolPermissions"; @@ -80,6 +88,7 @@ const CreateTeamModal = ({ const [modelsToPick, setModelsToPick] = useState([]); const [guardrailsList, setGuardrailsList] = useState([]); const [policiesList, setPoliciesList] = useState([]); + const [searchToolNames, setSearchToolNames] = useState([]); const [mcpAccessGroups, setMcpAccessGroups] = useState([]); const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false); @@ -158,6 +167,24 @@ const CreateTeamModal = ({ fetchPolicies(); }, [accessToken]); + useEffect(() => { + const loadSearchTools = async () => { + try { + if (!accessToken) return; + const response = await fetchSearchTools(accessToken); + const tools = Array.isArray(response?.data) ? response.data : []; + setSearchToolNames( + tools + .map((tool: any) => tool?.search_tool_name) + .filter((name: unknown): name is string => typeof name === "string" && name.length > 0), + ); + } catch (error) { + console.error("Failed to fetch search tools for team create modal:", error); + } + }; + loadSearchTools(); + }, [accessToken]); + const handleCreate = async (formValues: Record) => { try { console.log(`formValues: ${JSON.stringify(formValues)}`); @@ -395,6 +422,27 @@ const CreateTeamModal = ({
+ + Allowed Search Tools{" "} + + + + + } + name="allowed_search_tools" + > + ({ label: name, value: name }))} + showSearch + optionFilterProp="label" + /> + + Team Member Settings diff --git a/ui/litellm-dashboard/src/components/OldTeams.tsx b/ui/litellm-dashboard/src/components/OldTeams.tsx index 8349e271b8..c7f39c0dcd 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.tsx @@ -57,7 +57,14 @@ import type { KeyResponse, Team } from "./key_team_helpers/key_list"; import MCPServerSelector from "./mcp_server_management/MCPServerSelector"; import MCPToolPermissions from "./mcp_server_management/MCPToolPermissions"; import NotificationsManager from "./molecules/notifications_manager"; -import { Organization, fetchMCPAccessGroups, getGuardrailsList, getPoliciesList, teamDeleteCall } from "./networking"; +import { + Organization, + fetchMCPAccessGroups, + fetchSearchTools, + getGuardrailsList, + getPoliciesList, + teamDeleteCall, +} from "./networking"; import NumericalInput from "./shared/numerical_input"; import VectorStoreSelector from "./vector_store_management/VectorStoreSelector"; @@ -267,6 +274,7 @@ const Teams: React.FC = ({ // Add this state near the other useState declarations const [guardrailsList, setGuardrailsList] = useState([]); const [policiesList, setPoliciesList] = useState([]); + const [searchToolNames, setSearchToolNames] = useState([]); const [loggingSettings, setLoggingSettings] = useState([]); const [mcpAccessGroups, setMcpAccessGroups] = useState([]); const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false); @@ -334,6 +342,24 @@ const Teams: React.FC = ({ fetchPolicies(); }, [accessToken]); + useEffect(() => { + const loadSearchTools = async () => { + try { + if (!accessToken) return; + const response = await fetchSearchTools(accessToken); + const tools = Array.isArray(response?.data) ? response.data : []; + setSearchToolNames( + tools + .map((tool: any) => tool?.search_tool_name) + .filter((name: unknown): name is string => typeof name === "string" && name.length > 0), + ); + } catch (error) { + console.error("Failed to fetch search tools:", error); + } + }; + loadSearchTools(); + }, [accessToken]); + const fetchMcpAccessGroups = async () => { try { if (accessToken == null) { @@ -1207,6 +1233,27 @@ const Teams: React.FC = ({ /> + + Allowed Search Tools{" "} + + + + + } + name="allowed_search_tools" + > + ({ label: name, value: name }))} + showSearch + filterOption={(input, option) => + (option?.label ?? "").toLowerCase().includes(input.toLowerCase()) + } + /> + + @@ -1416,29 +1439,6 @@ const TeamInfoView: React.FC = ({ /> - { - if (!value || (typeof value === "string" && value.trim() === "")) { - return Promise.resolve(); - } - try { - JSON.parse(value); - return Promise.resolve(); - } catch (error) { - return Promise.reject(new Error("Please enter valid JSON")); - } - }, - }, - ]} - > - - - = ({ )} - {info.metadata?.search_provider_config && ( -
- Search Provider Configuration -
-                          {JSON.stringify(info.metadata.search_provider_config, null, 2)}
-                        
-
- )} )} From 77d48e739d1c63bbedd6c701a53502ce8fc1521c Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 28 Apr 2026 18:57:55 +0530 Subject: [PATCH 03/14] chore(proxy): remove unintended generated and local files from prior commit Drop accidental dashboard export path renames in _experimental/out, remove committed local proxy log, and remove the unintended ad-hoc test file so the feature commit only contains intentional source changes. Made-with: Cursor --- .../out/{404/index.html => 404.html} | 0 .../index.html => _not-found.html} | 0 .../index.html => api-reference.html} | 0 .../out/{chat/index.html => chat.html} | 0 .../index.html => api-playground.html} | 0 .../{budgets/index.html => budgets.html} | 0 .../{caching/index.html => caching.html} | 0 .../index.html => claude-code-plugins.html} | 0 .../{old-usage/index.html => old-usage.html} | 0 .../{prompts/index.html => prompts.html} | 0 .../index.html => tag-management.html} | 0 .../index.html => guardrails.html} | 0 .../out/{login/index.html => login.html} | 0 .../out/{logs/index.html => logs.html} | 0 .../{callback/index.html => callback.html} | 0 .../{model-hub/index.html => model-hub.html} | 0 .../{model_hub/index.html => model_hub.html} | 0 .../index.html => model_hub_table.html} | 0 .../index.html => models-and-endpoints.html} | 0 .../index.html => onboarding.html} | 0 .../index.html => organizations.html} | 0 .../index.html => playground.html} | 0 .../{policies/index.html => policies.html} | 0 .../index.html => admin-settings.html} | 0 .../index.html => logging-and-alerts.html} | 0 .../index.html => router-settings.html} | 0 .../{ui-theme/index.html => ui-theme.html} | 0 .../out/{skills/index.html => skills.html} | 0 .../out/{teams/index.html => teams.html} | 0 .../{test-key/index.html => test-key.html} | 0 .../index.html => mcp-servers.html} | 0 .../index.html => vector-stores.html} | 0 .../out/{usage/index.html => usage.html} | 0 .../out/{users/index.html => users.html} | 0 .../index.html => virtual-keys.html} | 0 proxy_server.log | 659 ------------------ tests/test_proxy_search_tool_auth.py | 212 ------ 37 files changed, 871 deletions(-) rename litellm/proxy/_experimental/out/{404/index.html => 404.html} (100%) rename litellm/proxy/_experimental/out/{_not-found/index.html => _not-found.html} (100%) rename litellm/proxy/_experimental/out/{api-reference/index.html => api-reference.html} (100%) rename litellm/proxy/_experimental/out/{chat/index.html => chat.html} (100%) rename litellm/proxy/_experimental/out/experimental/{api-playground/index.html => api-playground.html} (100%) rename litellm/proxy/_experimental/out/experimental/{budgets/index.html => budgets.html} (100%) rename litellm/proxy/_experimental/out/experimental/{caching/index.html => caching.html} (100%) rename litellm/proxy/_experimental/out/experimental/{claude-code-plugins/index.html => claude-code-plugins.html} (100%) rename litellm/proxy/_experimental/out/experimental/{old-usage/index.html => old-usage.html} (100%) rename litellm/proxy/_experimental/out/experimental/{prompts/index.html => prompts.html} (100%) rename litellm/proxy/_experimental/out/experimental/{tag-management/index.html => tag-management.html} (100%) rename litellm/proxy/_experimental/out/{guardrails/index.html => guardrails.html} (100%) rename litellm/proxy/_experimental/out/{login/index.html => login.html} (100%) rename litellm/proxy/_experimental/out/{logs/index.html => logs.html} (100%) rename litellm/proxy/_experimental/out/mcp/oauth/{callback/index.html => callback.html} (100%) rename litellm/proxy/_experimental/out/{model-hub/index.html => model-hub.html} (100%) rename litellm/proxy/_experimental/out/{model_hub/index.html => model_hub.html} (100%) rename litellm/proxy/_experimental/out/{model_hub_table/index.html => model_hub_table.html} (100%) rename litellm/proxy/_experimental/out/{models-and-endpoints/index.html => models-and-endpoints.html} (100%) rename litellm/proxy/_experimental/out/{onboarding/index.html => onboarding.html} (100%) rename litellm/proxy/_experimental/out/{organizations/index.html => organizations.html} (100%) rename litellm/proxy/_experimental/out/{playground/index.html => playground.html} (100%) rename litellm/proxy/_experimental/out/{policies/index.html => policies.html} (100%) rename litellm/proxy/_experimental/out/settings/{admin-settings/index.html => admin-settings.html} (100%) rename litellm/proxy/_experimental/out/settings/{logging-and-alerts/index.html => logging-and-alerts.html} (100%) rename litellm/proxy/_experimental/out/settings/{router-settings/index.html => router-settings.html} (100%) rename litellm/proxy/_experimental/out/settings/{ui-theme/index.html => ui-theme.html} (100%) rename litellm/proxy/_experimental/out/{skills/index.html => skills.html} (100%) rename litellm/proxy/_experimental/out/{teams/index.html => teams.html} (100%) rename litellm/proxy/_experimental/out/{test-key/index.html => test-key.html} (100%) rename litellm/proxy/_experimental/out/tools/{mcp-servers/index.html => mcp-servers.html} (100%) rename litellm/proxy/_experimental/out/tools/{vector-stores/index.html => vector-stores.html} (100%) rename litellm/proxy/_experimental/out/{usage/index.html => usage.html} (100%) rename litellm/proxy/_experimental/out/{users/index.html => users.html} (100%) rename litellm/proxy/_experimental/out/{virtual-keys/index.html => virtual-keys.html} (100%) delete mode 100644 proxy_server.log delete mode 100644 tests/test_proxy_search_tool_auth.py diff --git a/litellm/proxy/_experimental/out/404/index.html b/litellm/proxy/_experimental/out/404.html similarity index 100% rename from litellm/proxy/_experimental/out/404/index.html rename to litellm/proxy/_experimental/out/404.html diff --git a/litellm/proxy/_experimental/out/_not-found/index.html b/litellm/proxy/_experimental/out/_not-found.html similarity index 100% rename from litellm/proxy/_experimental/out/_not-found/index.html rename to litellm/proxy/_experimental/out/_not-found.html diff --git a/litellm/proxy/_experimental/out/api-reference/index.html b/litellm/proxy/_experimental/out/api-reference.html similarity index 100% rename from litellm/proxy/_experimental/out/api-reference/index.html rename to litellm/proxy/_experimental/out/api-reference.html diff --git a/litellm/proxy/_experimental/out/chat/index.html b/litellm/proxy/_experimental/out/chat.html similarity index 100% rename from litellm/proxy/_experimental/out/chat/index.html rename to litellm/proxy/_experimental/out/chat.html diff --git a/litellm/proxy/_experimental/out/experimental/api-playground/index.html b/litellm/proxy/_experimental/out/experimental/api-playground.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/api-playground/index.html rename to litellm/proxy/_experimental/out/experimental/api-playground.html diff --git a/litellm/proxy/_experimental/out/experimental/budgets/index.html b/litellm/proxy/_experimental/out/experimental/budgets.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/budgets/index.html rename to litellm/proxy/_experimental/out/experimental/budgets.html diff --git a/litellm/proxy/_experimental/out/experimental/caching/index.html b/litellm/proxy/_experimental/out/experimental/caching.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/caching/index.html rename to litellm/proxy/_experimental/out/experimental/caching.html diff --git a/litellm/proxy/_experimental/out/experimental/claude-code-plugins/index.html b/litellm/proxy/_experimental/out/experimental/claude-code-plugins.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/claude-code-plugins/index.html rename to litellm/proxy/_experimental/out/experimental/claude-code-plugins.html diff --git a/litellm/proxy/_experimental/out/experimental/old-usage/index.html b/litellm/proxy/_experimental/out/experimental/old-usage.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/old-usage/index.html rename to litellm/proxy/_experimental/out/experimental/old-usage.html diff --git a/litellm/proxy/_experimental/out/experimental/prompts/index.html b/litellm/proxy/_experimental/out/experimental/prompts.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/prompts/index.html rename to litellm/proxy/_experimental/out/experimental/prompts.html diff --git a/litellm/proxy/_experimental/out/experimental/tag-management/index.html b/litellm/proxy/_experimental/out/experimental/tag-management.html similarity index 100% rename from litellm/proxy/_experimental/out/experimental/tag-management/index.html rename to litellm/proxy/_experimental/out/experimental/tag-management.html diff --git a/litellm/proxy/_experimental/out/guardrails/index.html b/litellm/proxy/_experimental/out/guardrails.html similarity index 100% rename from litellm/proxy/_experimental/out/guardrails/index.html rename to litellm/proxy/_experimental/out/guardrails.html diff --git a/litellm/proxy/_experimental/out/login/index.html b/litellm/proxy/_experimental/out/login.html similarity index 100% rename from litellm/proxy/_experimental/out/login/index.html rename to litellm/proxy/_experimental/out/login.html diff --git a/litellm/proxy/_experimental/out/logs/index.html b/litellm/proxy/_experimental/out/logs.html similarity index 100% rename from litellm/proxy/_experimental/out/logs/index.html rename to litellm/proxy/_experimental/out/logs.html diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/index.html b/litellm/proxy/_experimental/out/mcp/oauth/callback.html similarity index 100% rename from litellm/proxy/_experimental/out/mcp/oauth/callback/index.html rename to litellm/proxy/_experimental/out/mcp/oauth/callback.html diff --git a/litellm/proxy/_experimental/out/model-hub/index.html b/litellm/proxy/_experimental/out/model-hub.html similarity index 100% rename from litellm/proxy/_experimental/out/model-hub/index.html rename to litellm/proxy/_experimental/out/model-hub.html diff --git a/litellm/proxy/_experimental/out/model_hub/index.html b/litellm/proxy/_experimental/out/model_hub.html similarity index 100% rename from litellm/proxy/_experimental/out/model_hub/index.html rename to litellm/proxy/_experimental/out/model_hub.html diff --git a/litellm/proxy/_experimental/out/model_hub_table/index.html b/litellm/proxy/_experimental/out/model_hub_table.html similarity index 100% rename from litellm/proxy/_experimental/out/model_hub_table/index.html rename to litellm/proxy/_experimental/out/model_hub_table.html diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/index.html b/litellm/proxy/_experimental/out/models-and-endpoints.html similarity index 100% rename from litellm/proxy/_experimental/out/models-and-endpoints/index.html rename to litellm/proxy/_experimental/out/models-and-endpoints.html diff --git a/litellm/proxy/_experimental/out/onboarding/index.html b/litellm/proxy/_experimental/out/onboarding.html similarity index 100% rename from litellm/proxy/_experimental/out/onboarding/index.html rename to litellm/proxy/_experimental/out/onboarding.html diff --git a/litellm/proxy/_experimental/out/organizations/index.html b/litellm/proxy/_experimental/out/organizations.html similarity index 100% rename from litellm/proxy/_experimental/out/organizations/index.html rename to litellm/proxy/_experimental/out/organizations.html diff --git a/litellm/proxy/_experimental/out/playground/index.html b/litellm/proxy/_experimental/out/playground.html similarity index 100% rename from litellm/proxy/_experimental/out/playground/index.html rename to litellm/proxy/_experimental/out/playground.html diff --git a/litellm/proxy/_experimental/out/policies/index.html b/litellm/proxy/_experimental/out/policies.html similarity index 100% rename from litellm/proxy/_experimental/out/policies/index.html rename to litellm/proxy/_experimental/out/policies.html diff --git a/litellm/proxy/_experimental/out/settings/admin-settings/index.html b/litellm/proxy/_experimental/out/settings/admin-settings.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/admin-settings/index.html rename to litellm/proxy/_experimental/out/settings/admin-settings.html diff --git a/litellm/proxy/_experimental/out/settings/logging-and-alerts/index.html b/litellm/proxy/_experimental/out/settings/logging-and-alerts.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/logging-and-alerts/index.html rename to litellm/proxy/_experimental/out/settings/logging-and-alerts.html diff --git a/litellm/proxy/_experimental/out/settings/router-settings/index.html b/litellm/proxy/_experimental/out/settings/router-settings.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/router-settings/index.html rename to litellm/proxy/_experimental/out/settings/router-settings.html diff --git a/litellm/proxy/_experimental/out/settings/ui-theme/index.html b/litellm/proxy/_experimental/out/settings/ui-theme.html similarity index 100% rename from litellm/proxy/_experimental/out/settings/ui-theme/index.html rename to litellm/proxy/_experimental/out/settings/ui-theme.html diff --git a/litellm/proxy/_experimental/out/skills/index.html b/litellm/proxy/_experimental/out/skills.html similarity index 100% rename from litellm/proxy/_experimental/out/skills/index.html rename to litellm/proxy/_experimental/out/skills.html diff --git a/litellm/proxy/_experimental/out/teams/index.html b/litellm/proxy/_experimental/out/teams.html similarity index 100% rename from litellm/proxy/_experimental/out/teams/index.html rename to litellm/proxy/_experimental/out/teams.html diff --git a/litellm/proxy/_experimental/out/test-key/index.html b/litellm/proxy/_experimental/out/test-key.html similarity index 100% rename from litellm/proxy/_experimental/out/test-key/index.html rename to litellm/proxy/_experimental/out/test-key.html diff --git a/litellm/proxy/_experimental/out/tools/mcp-servers/index.html b/litellm/proxy/_experimental/out/tools/mcp-servers.html similarity index 100% rename from litellm/proxy/_experimental/out/tools/mcp-servers/index.html rename to litellm/proxy/_experimental/out/tools/mcp-servers.html diff --git a/litellm/proxy/_experimental/out/tools/vector-stores/index.html b/litellm/proxy/_experimental/out/tools/vector-stores.html similarity index 100% rename from litellm/proxy/_experimental/out/tools/vector-stores/index.html rename to litellm/proxy/_experimental/out/tools/vector-stores.html diff --git a/litellm/proxy/_experimental/out/usage/index.html b/litellm/proxy/_experimental/out/usage.html similarity index 100% rename from litellm/proxy/_experimental/out/usage/index.html rename to litellm/proxy/_experimental/out/usage.html diff --git a/litellm/proxy/_experimental/out/users/index.html b/litellm/proxy/_experimental/out/users.html similarity index 100% rename from litellm/proxy/_experimental/out/users/index.html rename to litellm/proxy/_experimental/out/users.html diff --git a/litellm/proxy/_experimental/out/virtual-keys/index.html b/litellm/proxy/_experimental/out/virtual-keys.html similarity index 100% rename from litellm/proxy/_experimental/out/virtual-keys/index.html rename to litellm/proxy/_experimental/out/virtual-keys.html diff --git a/proxy_server.log b/proxy_server.log deleted file mode 100644 index 381b28a784..0000000000 --- a/proxy_server.log +++ /dev/null @@ -1,659 +0,0 @@ -:128: RuntimeWarning: 'litellm.proxy.proxy_cli' found in sys.modules after import of package 'litellm.proxy', but prior to execution of 'litellm.proxy.proxy_cli'; this may result in unpredictable behaviour -2026-04-28 18:52:10,288 - litellm_proxy_extras - INFO - Running prisma migrate deploy -2026-04-28 18:52:13,736 - litellm_proxy_extras - INFO - prisma migrate deploy stdout: Environment variables loaded from ../../.env -Prisma schema loaded from schema.prisma -Datasource "client": PostgreSQL database "litellm", schema "public" at "localhost:5432" - -118 migrations found in prisma/migrations - - -No pending migrations to apply. - -2026-04-28 18:52:13,737 - litellm_proxy_extras - INFO - prisma migrate deploy completed -2026-04-28 18:52:13,737 - litellm_proxy_extras - INFO - No pending migrations — skipping post-migration sanity check -INFO: Started server process [19856] -INFO: Waiting for application startup. -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:803 - litellm.proxy.proxy_server.py::startup() - CHECKING PREMIUM USER - True -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:816 - worker_config: {"model": null, "alias": null, "api_base": null, "api_version": "2025-02-01-preview", "debug": false, "detailed_debug": true, "temperature": null, "max_tokens": null, "request_timeout": null, "max_budget": null, "telemetry": true, "drop_params": false, "add_function_to_prompt": false, "headers": null, "save": false, "config": "proxy_server_config.yaml", "use_queue": false} -LiteLLM Proxy: Using default (v1) migration resolver. If your deployment has seen schema thrashing during rolling deploys, try --use_v2_migration_resolver (safer: avoids the diff-and-force recovery that caused the thrash). - - ██╗ ██╗████████╗███████╗██╗ ██╗ ███╗ ███╗ - ██║ ██║╚══██╔══╝██╔════╝██║ ██║ ████╗ ████║ - ██║ ██║ ██║ █████╗ ██║ ██║ ██╔████╔██║ - ██║ ██║ ██║ ██╔══╝ ██║ ██║ ██║╚██╔╝██║ - ███████╗██║ ██║ ███████╗███████╗███████╗██║ ╚═╝ ██║ - ╚══════╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ - -18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'combined_model_name': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'stripped_model_name': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'combined_stripped_model_name': '170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd', 'custom_llm_provider': None} -18:52:13 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -18:52:13 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd in litellm.model_cost: 170fb9c8e18f87663825b69e7e54393b7a3392d1ecbfb0d6a8a543f0723173fd -18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} -18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} -18:52:13 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'openai/gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} -18:52:13 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=gpt-5.3-codex in litellm.model_cost: gpt-5.3-codex -18:52:13 - LiteLLM Router:DEBUG: router.py:7223 - -Initialized Model List ['gpt-5.3-codex'] -18:52:13 - LiteLLM Router:INFO: router.py:812 - Routing strategy: simple-shuffle -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:3915 - Policy engine: no policies in config, skipping -18:52:13 - LiteLLM Proxy:DEBUG: utils.py:2460 - Creating Prisma Client.. -18:52:13 - LiteLLM Proxy:DEBUG: utils.py:2527 - Success - Created Prisma Client -18:52:13 - LiteLLM Proxy:DEBUG: utils.py:3745 - PrismaClient: connect() called Attempting to Connect to DB -18:52:13 - LiteLLM Proxy:DEBUG: utils.py:3749 - PrismaClient: DB not connected, Attempting to Connect to DB -query-engine ac9d7041ed77bcc8a8dbd2ab6616b39013829574 -18:52:13 - LiteLLM Proxy:DEBUG: prisma_client.py:247 - IAM token auth not enabled, skipping token refresh task -18:52:13 - LiteLLM Proxy:INFO: utils.py:4307 - Started Prisma DB health watchdog (interval=30s, reconnect_cooldown=15s, probe_timeout=5.0s, reconnect_timeout=30.0s) -18:52:13 - LiteLLM Proxy:INFO: utils.py:4062 - Found prisma-query-engine at PID 20355. -18:52:13 - LiteLLM Proxy:INFO: utils.py:4066 - Watching engine PID 20355 via waitpid thread. -18:52:13 - LiteLLM:DEBUG: logging_callback_manager.py:336 - Custom logger of type SkillsInjectionHook, key: SkillsInjectionHook-max_iterations=10-sandbox_timeout=120-message_logging=True-turn_off_message_logging=False already exists in [, , , , , , ], not adding again.. -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:895 - About to initialize semantic tool filter -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:898 - litellm_settings keys = [] -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:6220 - Semantic tool filter not configured or not enabled, skipping initialization -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:905 - After semantic tool filter initialization -18:52:13 - LiteLLM Proxy:DEBUG: proxy_server.py:919 - prisma_client: -18:52:13 - LiteLLM Proxy:INFO: proxy_server.py:6467 - Tag spend update job scheduled at 25s interval (2.3x main job interval) -18:52:13 - LiteLLM Proxy:DEBUG: hanging_request_check.py:148 - Checking for hanging requests.... -18:52:13 - LiteLLM Proxy:INFO: utils.py:5083 - Starting spend logs queue monitor (threshold: 100, poll_interval: 2.0s) -18:52:14 - LiteLLM Proxy:INFO: utils.py:2634 - All necessary views exist! -18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:874 - Password migration: No plaintext passwords found -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:4256 - len new_models: 2 -18:52:14 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': '88d4dde8-817a-4c20-9bec-442961096d25', 'combined_model_name': '88d4dde8-817a-4c20-9bec-442961096d25', 'stripped_model_name': '88d4dde8-817a-4c20-9bec-442961096d25', 'combined_stripped_model_name': '88d4dde8-817a-4c20-9bec-442961096d25', 'custom_llm_provider': None} -18:52:14 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -18:52:14 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=88d4dde8-817a-4c20-9bec-442961096d25 in litellm.model_cost: 88d4dde8-817a-4c20-9bec-442961096d25 -18:52:14 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'combined_model_name': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'stripped_model_name': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'combined_stripped_model_name': '2ab3179b-62e9-4720-9ba7-0a2f12536cfa', 'custom_llm_provider': None} -18:52:14 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -18:52:14 - LiteLLM:DEBUG: utils.py:2900 - added/updated model=2ab3179b-62e9-4720-9ba7-0a2f12536cfa in litellm.model_cost: 2ab3179b-62e9-4720-9ba7-0a2f12536cfa -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5461 - guardrails from the DB [] -18:52:14 - LiteLLM Proxy:INFO: policy_registry.py:577 - Synced 0 production policies and 0 draft/published (by ID) from DB to in-memory registry -18:52:14 - LiteLLM Proxy:INFO: attachment_registry.py:481 - Synced 0 attachments from DB to in-memory registry -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5497 - Successfully synced policies and attachments from DB -18:52:14 - LiteLLM:DEBUG: mcp_server_manager.py:2644 - Loading MCP servers from database into registry... -18:52:14 - LiteLLM:INFO: mcp_server_manager.py:2664 - Found 1 MCP servers in database -18:52:14 - LiteLLM:DEBUG: mcp_server_manager.py:2688 - Building server from DB: 28a195c6-0224-4765-af9b-46f7a7f65ccb (deepwiki) -18:52:14 - LiteLLM:DEBUG: mcp_server_manager.py:2697 - MCP registry refreshed (1 servers in registry) -18:52:14 - LiteLLM Proxy:DEBUG: pass_through_endpoints.py:2409 - initializing pass through endpoints -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.weave -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.litellm_agent -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.dotprompt -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.dotprompt: ['dotprompt'] -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.gitlab -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.gitlab: ['gitlab'] -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.azure_sentinel -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.arize -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.arize: ['arize_phoenix'] -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.agentops -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.compression_interception -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.focus -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.prometheus_helpers -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.generic_prompt_management -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.generic_prompt_management: ['generic_prompt_management'] -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.levo -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.websearch_interception -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.deepeval -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.bitbucket -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:65 - Found prompt_initializer_registry in litellm.integrations.bitbucket: ['bitbucket'] -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:54 - Discovering prompt integrations in: litellm.integrations.vantage -18:52:14 - LiteLLM Proxy:DEBUG: prompt_registry.py:76 - Discovered 5 prompt initializers: ['dotprompt', 'gitlab', 'arize_phoenix', 'generic_prompt_management', 'bitbucket'] -18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:5640 - Loading 0 search tool(s) from database into router -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5660 - No search tools found in database, keeping config-loaded search tools (if any) -18:52:14 - LiteLLM Proxy:INFO: tool_registry_writer.py:329 - ToolPolicyRegistry: synced 17 tool policies and 1 object permissions from DB -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:5517 - Successfully synced tool policy from DB -18:52:14 - LiteLLM:DEBUG: focus_logger.py:167 - No Focus export logger registered; skipping scheduler -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6759 - key_rotation_enabled: False -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6793 - Key rotation disabled (set LITELLM_KEY_ROTATION_ENABLED=true to enable) -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6824 - expired_ui_session_key_cleanup_enabled: False -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:6869 - Expired UI session key cleanup disabled (set LITELLM_EXPIRED_UI_SESSION_KEY_CLEANUP_ENABLED=true to enable) -18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:6622 - Batch cost check job scheduled successfully -18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:6653 - Responses cost check job scheduled successfully -18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:6672 - APScheduler started with memory leak prevention settings: removed jitter, increased intervals, misfire_grace_time=3600 -18:52:14 - LiteLLM Proxy:DEBUG: proxy_server.py:7036 - LiteLLM: Pyroscope profiling is disabled (set LITELLM_ENABLE_PYROSCOPE=true to enable). -18:52:14 - LiteLLM Proxy:INFO: proxy_server.py:755 - SESSION REUSE: Created shared aiohttp session for connection pooling (ID: 6165409104, limit=1000, limit_per_host=500) -INFO: Application startup complete. -INFO: Uvicorn running on http://0.0.0.0:4000 (Press CTRL+C to quit) -18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. -18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e -18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. -18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e -18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list -18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. -18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e -18:52:21 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/key/list -18:52:21 - LiteLLM:DEBUG: user_api_key_auth.py:1023 - api key not found in cache. -18:52:21 - LiteLLM Proxy:DEBUG: utils.py:3061 - PrismaClient: find_unique for token: 00f60cfa9df317dade7cb93c0bdb83b44250c865daf6920ea0de433d523a894e - -#------------------------------------------------------------# -# # -# 'The thing I wish you improved is...' # -# https://github.com/BerriAI/litellm/issues/new # -# # -#------------------------------------------------------------# - - Thank you for using LiteLLM! - Krrish & Ishaan - - - -Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new - - -LiteLLM: Proxy initialized with Config, Set models: - gpt-5.3-codex -INFO: 127.0.0.1:65291 - "GET /project/list HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:65293 - "HEAD / HTTP/1.1" 200 OK -INFO: 127.0.0.1:65293 - "GET /__next._tree.txt?_rsc=1r34m HTTP/1.1" 404 Not Found -18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.984645+00:00 -18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.987707+00:00 -18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.988286+00:00 -18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:21.988897+00:00 -18:52:21 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:22 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:22 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:22 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4526 - Entering list_keys function -18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4950 - Filter conditions: {'OR': [{'team_id': None}, {'team_id': {'not': 'litellm-dashboard'}}]} -18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5020 - Pagination: skip=0, take=50 -18:52:22 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65283 - "GET /organization/list HTTP/1.1" 200 OK -18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5062 - Fetched 4 keys -18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5074 - Total count of keys: 4 -INFO: 127.0.0.1:65285 - "GET /team/list HTTP/1.1" 200 OK -18:52:22 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4604 - Successfully prepared response -INFO: 127.0.0.1:65290 - "GET /key/list?page=1&size=50&sort_by=created_at&sort_order=desc&expand=user&return_full_object=true&include_team_keys=true&include_created_by_keys=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:65288 - "GET /tag/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65290 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:24 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:52:24 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:52:24 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:52:24 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 -INFO: 127.0.0.1:65290 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.414705+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.416982+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/models -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.426995+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v2/user/info -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.430882+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/access_group -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.434375+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.437819+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -INFO: 127.0.0.1:65326 - "GET /v2/user/info HTTP/1.1" 200 OK -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/access_groups -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.460826+00:00 -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65324 - "GET /models?include_model_access_groups=True&return_wildcard_routes=True&scope=expand HTTP/1.1" 200 OK -18:52:28 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/toolset -18:52:28 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:28.463333+00:00 -INFO: 127.0.0.1:65290 - "GET /organization/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65328 - "GET /v1/access_group HTTP/1.1" 200 OK -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers -INFO: 127.0.0.1:65329 - "GET /v1/mcp/server HTTP/1.1" 200 OK -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:28 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -INFO: 127.0.0.1:65321 - "GET /team/list HTTP/1.1" 200 OK -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:28 - LiteLLM Proxy:WARNING: toolset_db.py:60 - litellm.proxy._experimental.mcp_server.toolset_db::list_mcp_toolsets - 'Prisma' object has no attribute 'litellm_mcptoolsettable' -INFO: 127.0.0.1:65324 - "GET /v1/mcp/toolset HTTP/1.1" 200 OK -INFO: 127.0.0.1:65326 - "GET /v1/mcp/access_groups HTTP/1.1" 200 OK -18:52:30 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:52:30 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:30.307066+00:00 -18:52:30 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:52:30 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:30.310005+00:00 -INFO: 127.0.0.1:65321 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:30 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list -18:52:30 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:30.314509+00:00 -18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:30 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:30 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:30 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:30 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65326 - "GET /organization/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65324 - "GET /team/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65329 - "GET /tag/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65329 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:34 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:52:34 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:34.018157+00:00 -18:52:34 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:52:34 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:34.038105+00:00 -18:52:34 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list -18:52:34 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:34.045120+00:00 -INFO: 127.0.0.1:65321 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:34 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:34 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:34 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:34 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65329 - "GET /organization/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65324 - "GET /team/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65326 - "GET /tag/list HTTP/1.1" 200 OK -18:52:35 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:52:35 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:52:35 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:52:35 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 -INFO: 127.0.0.1:65326 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:39 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:4256 - len new_models: 2 -18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5461 - guardrails from the DB [] -18:52:44 - LiteLLM Proxy:INFO: policy_registry.py:577 - Synced 0 production policies and 0 draft/published (by ID) from DB to in-memory registry -18:52:44 - LiteLLM Proxy:INFO: attachment_registry.py:481 - Synced 0 attachments from DB to in-memory registry -18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5497 - Successfully synced policies and attachments from DB -18:52:44 - LiteLLM:DEBUG: mcp_server_manager.py:2644 - Loading MCP servers from database into registry... -18:52:44 - LiteLLM:INFO: mcp_server_manager.py:2664 - Found 1 MCP servers in database -18:52:44 - LiteLLM:DEBUG: mcp_server_manager.py:2697 - MCP registry refreshed (1 servers in registry) -18:52:44 - LiteLLM Proxy:DEBUG: pass_through_endpoints.py:2409 - initializing pass through endpoints -18:52:44 - LiteLLM Proxy:INFO: proxy_server.py:5640 - Loading 0 search tool(s) from database into router -18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5660 - No search tools found in database, keeping config-loaded search tools (if any) -18:52:44 - LiteLLM Proxy:INFO: tool_registry_writer.py:329 - ToolPolicyRegistry: synced 17 tool policies and 1 object permissions from DB -18:52:44 - LiteLLM Proxy:DEBUG: proxy_server.py:5517 - Successfully synced tool policy from DB -18:52:46 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:52:46 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:52:46 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:52:46 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 -18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.075515+00:00 -18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.133027+00:00 -INFO: 127.0.0.1:65389 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list -18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.284965+00:00 -18:52:54 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/key/list -18:52:54 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:22:54.295543+00:00 -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:54 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4526 - Entering list_keys function -18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4950 - Filter conditions: {'OR': [{'team_id': None}, {'team_id': {'not': 'litellm-dashboard'}}]} -18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5020 - Pagination: skip=0, take=50 -18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:52:54 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65385 - "GET /organization/list HTTP/1.1" 200 OK -18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5062 - Fetched 4 keys -INFO: 127.0.0.1:65392 - "GET /tag/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65387 - "GET /team/list HTTP/1.1" 200 OK -18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:5074 - Total count of keys: 4 -18:52:54 - LiteLLM Proxy:DEBUG: key_management_endpoints.py:4604 - Successfully prepared response -INFO: 127.0.0.1:65393 - "GET /key/list?page=1&size=50&sort_by=created_at&sort_order=desc&expand=user&return_full_object=true&include_team_keys=true&include_created_by_keys=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:65393 - "GET /project/list HTTP/1.1" 404 Not Found -18:52:58 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:52:58 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:52:58 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:52:58 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 -18:53:04 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:05 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/health/readiness -18:53:05 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:53:05 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:05 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:05.652862+00:00 -18:53:06 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:53:06 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:06.042541+00:00 -18:53:06 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/tag/list -18:53:06 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:06.085990+00:00 -INFO: 127.0.0.1:65443 - "GET /project/list HTTP/1.1" 404 Not Found -18:53:06 - LiteLLM:DEBUG: http_handler.py:840 - Using AiohttpTransport... -INFO: 127.0.0.1:65436 - "GET /health/readiness HTTP/1.1" 200 OK -18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:06 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:06 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:06 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:06 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65438 - "GET /organization/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65442 - "GET /tag/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65440 - "GET /team/list HTTP/1.1" 200 OK -18:53:08 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:53:09 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:53:09 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:53:09 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.447694+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/toolset -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.480143+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/get/mcp_semantic_filter_settings -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.511525+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/model_group/info -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.521031+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/config/list -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.525741+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/network/client-ip -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.533092+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65461 - "GET /v1/mcp/network/client-ip HTTP/1.1" 200 OK -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:09 - LiteLLM Proxy:DEBUG: model_checks.py:131 - ALL KEY MODELS - 0 -18:53:09 - LiteLLM Proxy:DEBUG: model_checks.py:166 - ALL TEAM MODELS - 0 -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gpt-5.3-codex', 'combined_model_name': 'openai/gpt-5.3-codex', 'stripped_model_name': 'openai/gpt-5.3-codex', 'combined_stripped_model_name': 'openai/gpt-5.3-codex', 'custom_llm_provider': 'openai'} -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} -18:53:09 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} -18:53:09 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gemini-3.1-flash-image-preview', 'combined_model_name': 'vertex_ai/gemini-3.1-flash-image-preview', 'stripped_model_name': 'gemini-3.1-flash-image-preview', 'combined_stripped_model_name': 'vertex_ai/gemini-3.1-flash-image-preview', 'custom_llm_provider': 'vertex_ai'} -18:53:09 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'gemini-3.1-flash-image-preview', 'combined_model_name': 'gemini-3.1-flash-image-preview', 'stripped_model_name': 'gemini-3.1-flash-image-preview', 'combined_stripped_model_name': 'gemini-3.1-flash-image-preview', 'custom_llm_provider': 'vertex_ai'} -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server/submissions -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.669506+00:00 -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:09 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers -INFO: 127.0.0.1:65440 - "GET /v1/mcp/server HTTP/1.1" 200 OK -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:09 - LiteLLM Proxy:WARNING: toolset_db.py:60 - litellm.proxy._experimental.mcp_server.toolset_db::list_mcp_toolsets - 'Prisma' object has no attribute 'litellm_mcptoolsettable' -INFO: 127.0.0.1:65442 - "GET /v1/mcp/toolset HTTP/1.1" 200 OK -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server/health -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.692446+00:00 -INFO: 127.0.0.1:65442 - "HEAD / HTTP/1.1" 200 OK -INFO: 127.0.0.1:65436 - "GET /model_group/info HTTP/1.1" 200 OK -INFO: 127.0.0.1:65443 - "GET /config/list?config_type=general_settings HTTP/1.1" 200 OK -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/config/list -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.731878+00:00 -INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/github.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/slack.svg HTTP/1.1" 304 Not Modified -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/notion.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/linear.svg HTTP/1.1" 304 Not Modified -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/jira.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65438 - "GET /get/mcp_semantic_filter_settings HTTP/1.1" 200 OK -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/figma.svg HTTP/1.1" 304 Not Modified -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:09 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers -18:53:09 - LiteLLM:DEBUG: client.py:273 - litellm headers for streamable_http_client: {} -18:53:09 - LiteLLM:DEBUG: client.py:402 - MCP client using SSL configuration: SSLContext -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65436 - "GET /ui/assets/logos/gmail.svg HTTP/1.1" 304 Not Modified -18:53:09 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/model_group/info -18:53:09 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:09 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:09.989425+00:00 -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/stripe.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/google_drive.svg HTTP/1.1" 304 Not Modified -18:53:09 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/shopify.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/salesforce.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65442 - "GET /config/list?config_type=general_settings HTTP/1.1" 200 OK -INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/hubspot.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/twilio.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65461 - "GET /v1/mcp/server/submissions HTTP/1.1" 200 OK -INFO: 127.0.0.1:65442 - "GET /ui/assets/logos/cloudflare.svg HTTP/1.1" 304 Not Modified -18:53:10 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/postgresql.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/sentry.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65461 - "GET /ui/assets/logos/snowflake.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65442 - "GET /ui/assets/logos/zapier.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65461 - "GET /__next._tree.txt?_rsc=1r34m HTTP/1.1" 404 Not Found -18:53:10 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:10 - LiteLLM Proxy:DEBUG: model_checks.py:131 - ALL KEY MODELS - 0 -18:53:10 - LiteLLM Proxy:DEBUG: model_checks.py:166 - ALL TEAM MODELS - 0 -18:53:10 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} -18:53:10 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -18:53:10 - LiteLLM:DEBUG: utils.py:5620 - checking potential_model_names in litellm.model_cost: {'split_model': 'oia-gpt-realtime', 'combined_model_name': 'azure/oia-gpt-realtime', 'stripped_model_name': 'azure/oia-gpt-realtime', 'combined_stripped_model_name': 'azure/oia-gpt-realtime', 'custom_llm_provider': 'azure'} -18:53:10 - LiteLLM:DEBUG: utils.py:5921 - Error getting model info: This model isn't mapped yet. Add it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json -INFO: 127.0.0.1:65443 - "GET /ui/assets/logos/gitlab.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65438 - "GET /ui/assets/logos/google.svg HTTP/1.1" 304 Not Modified -INFO: 127.0.0.1:65436 - "GET /model_group/info HTTP/1.1" 200 OK -INFO: 127.0.0.1:65440 - "GET /v1/mcp/server/health HTTP/1.1" 200 OK -INFO: 127.0.0.1:65440 - "GET / HTTP/1.1" 200 OK -18:53:17 - LiteLLM Proxy:DEBUG: custom_openapi_spec.py:311 - Successfully added ProxyChatCompletionRequest schema to OpenAPI spec -18:53:17 - LiteLLM Proxy:DEBUG: custom_openapi_spec.py:311 - Successfully added EmbeddingRequest schema to OpenAPI spec -18:53:17 - LiteLLM Proxy:DEBUG: custom_openapi_spec.py:315 - Could not get schema for ResponsesAPIRequestParams -INFO: 127.0.0.1:65440 - "GET /openapi.json HTTP/1.1" 200 OK -18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/organization/list -18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.073159+00:00 -18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/team/list -18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.085893+00:00 -18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server -18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.089077+00:00 -18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/server/health -18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.093754+00:00 -18:53:17 - LiteLLM Proxy:DEBUG: http_parsing_utils.py:529 - populate_request_with_path_params: No vector_store_id present in path=/v1/mcp/toolset -18:53:17 - LiteLLM:DEBUG: user_api_key_auth.py:1099 - 404: {'error': "Team doesn't exist in cache + check_cache_only=True. Team=litellm-dashboard."} -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1348 - Checking if token expired, expiry time 2026-04-29 11:16:22.190000+00:00 and current time 2026-04-28 13:23:17.096736+00:00 -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers -INFO: 127.0.0.1:65438 - "GET /v1/mcp/server HTTP/1.1" 200 OK -18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:17 - LiteLLM Proxy:DEBUG: user_api_key_auth.py:1567 - centralized auth: team fetch failed (HTTPException: 404: {'error': "Team doesn't exist in db. Team=litellm-dashboard. Create team via `/team/new` call."}) -18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:4256 - len new_models: 2 -18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:796 - Admin user without explicit object_permission - returning all servers -18:53:17 - LiteLLM:DEBUG: client.py:273 - litellm headers for streamable_http_client: {} -18:53:17 - LiteLLM:DEBUG: client.py:402 - MCP client using SSL configuration: SSLContext -18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -18:53:17 - LiteLLM Proxy:WARNING: toolset_db.py:60 - litellm.proxy._experimental.mcp_server.toolset_db::list_mcp_toolsets - 'Prisma' object has no attribute 'litellm_mcptoolsettable' -INFO: 127.0.0.1:65442 - "GET /v1/mcp/toolset HTTP/1.1" 200 OK -18:53:17 - LiteLLM Proxy:DEBUG: auth_checks.py:4036 - Vector store registry not found, skipping vector store access check -INFO: 127.0.0.1:65436 - "GET /organization/list HTTP/1.1" 200 OK -INFO: 127.0.0.1:65443 - "GET /team/list HTTP/1.1" 200 OK -18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5461 - guardrails from the DB [] -18:53:17 - LiteLLM Proxy:INFO: policy_registry.py:577 - Synced 0 production policies and 0 draft/published (by ID) from DB to in-memory registry -18:53:17 - LiteLLM Proxy:INFO: attachment_registry.py:481 - Synced 0 attachments from DB to in-memory registry -18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5497 - Successfully synced policies and attachments from DB -18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:2644 - Loading MCP servers from database into registry... -18:53:17 - LiteLLM:INFO: mcp_server_manager.py:2664 - Found 1 MCP servers in database -18:53:17 - LiteLLM:DEBUG: mcp_server_manager.py:2697 - MCP registry refreshed (1 servers in registry) -18:53:17 - LiteLLM Proxy:DEBUG: pass_through_endpoints.py:2409 - initializing pass through endpoints -18:53:17 - LiteLLM Proxy:INFO: proxy_server.py:5640 - Loading 0 search tool(s) from database into router -18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5660 - No search tools found in database, keeping config-loaded search tools (if any) -18:53:17 - LiteLLM Proxy:INFO: tool_registry_writer.py:329 - ToolPolicyRegistry: synced 17 tool policies and 1 object permissions from DB -18:53:17 - LiteLLM Proxy:DEBUG: proxy_server.py:5517 - Successfully synced tool policy from DB -INFO: 127.0.0.1:65461 - "GET /v1/mcp/server/health HTTP/1.1" 200 OK -18:53:19 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:53:20 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:53:20 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:53:20 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 -18:53:29 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: spend_update_queue.py:39 - Aggregating updates by entity type: [] -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1122 - User Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1165 - End-User Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1180 - KEY Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1221 - Team Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1269 - Team Membership Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1342 - Org Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Tag Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1435 - Agent Spend transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily User Spend transactions: 0 -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily user spend update -18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Team Spend transactions: 0 -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily team spend update -18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Org Spend transactions: 0 -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily org spend update -18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily End_user Spend transactions: 0 -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily end_user spend update -18:53:31 - LiteLLM Proxy:DEBUG: daily_spend_update_queue.py:99 - Aggregated daily spend update transactions: {} -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1576 - Daily Agent Spend transactions: 0 -18:53:31 - LiteLLM Proxy:DEBUG: db_spend_update_writer.py:1609 - No new transactions to process for daily agent spend update -18:53:31 - LiteLLM Proxy:DEBUG: utils.py:4936 - Spend Logs transactions: 0 diff --git a/tests/test_proxy_search_tool_auth.py b/tests/test_proxy_search_tool_auth.py deleted file mode 100644 index 502bc655ce..0000000000 --- a/tests/test_proxy_search_tool_auth.py +++ /dev/null @@ -1,212 +0,0 @@ -""" -Test search tool authorization - verify model-like access control for search tools. - -Tests that: -1. Keys can only access search tools in their allowed_search_tools list -2. Teams can only access search tools in their allowed_search_tools list -3. Empty allowlists grant access to all search tools -4. Credentials are never exposed in team/key metadata -""" - -import pytest -from unittest.mock import MagicMock, patch -from fastapi import HTTPException - -# Import types and functions to test -from litellm.proxy._types import LiteLLM_TeamTable, UserAPIKeyAuth -from litellm.proxy.auth.auth_checks import ( - can_key_call_search_tool, - can_team_call_search_tool, -) - - -@pytest.mark.asyncio -async def test_key_can_access_allowed_search_tool(): - """Test that a key can access a search tool in its allowlist.""" - # Create a mock key with allowed_search_tools - mock_key = UserAPIKeyAuth( - token="sk-test-key", - models=["gpt-4"], - allowed_search_tools=["tavily-search", "perplexity-search"], - ) - - # Should succeed - tool is in allowlist - result = await can_key_call_search_tool( - search_tool_name="tavily-search", - valid_token=mock_key, - ) - assert result is True - - -@pytest.mark.asyncio -async def test_key_denied_non_allowed_search_tool(): - """Test that a key is denied access to a search tool not in its allowlist.""" - mock_key = UserAPIKeyAuth( - token="sk-test-key", - models=["gpt-4"], - allowed_search_tools=["tavily-search"], # Only tavily allowed - ) - - # Should raise exception - brave-search not in allowlist - with pytest.raises(Exception) as exc_info: - await can_key_call_search_tool( - search_tool_name="brave-search", - valid_token=mock_key, - ) - assert "not allowed to access search tool" in str(exc_info.value) - assert "brave-search" in str(exc_info.value) - - -@pytest.mark.asyncio -async def test_key_empty_allowlist_grants_all_access(): - """Test that an empty allowlist grants access to all search tools.""" - mock_key = UserAPIKeyAuth( - token="sk-test-key", - models=["gpt-4"], - allowed_search_tools=[], # Empty = all allowed - ) - - # Should succeed - empty list allows all - result = await can_key_call_search_tool( - search_tool_name="any-search-tool", - valid_token=mock_key, - ) - assert result is True - - -@pytest.mark.asyncio -async def test_team_can_access_allowed_search_tool(): - """Test that a team can access a search tool in its allowlist.""" - mock_team = LiteLLM_TeamTable( - team_id="team-123", - team_alias="Marketing Team", - models=["gpt-4"], - allowed_search_tools=["tavily-search", "exa-search"], - ) - - # Should succeed - tool is in allowlist - result = await can_team_call_search_tool( - search_tool_name="tavily-search", - team_object=mock_team, - ) - assert result is True - - -@pytest.mark.asyncio -async def test_team_denied_non_allowed_search_tool(): - """Test that a team is denied access to a search tool not in its allowlist.""" - mock_team = LiteLLM_TeamTable( - team_id="team-123", - team_alias="Engineering Team", - models=["gpt-4"], - allowed_search_tools=["perplexity-search"], # Only perplexity allowed - ) - - # Should raise exception - tavily-search not in allowlist - with pytest.raises(Exception) as exc_info: - await can_team_call_search_tool( - search_tool_name="tavily-search", - team_object=mock_team, - ) - assert "not allowed to access search tool" in str(exc_info.value) - assert "tavily-search" in str(exc_info.value) - - -@pytest.mark.asyncio -async def test_team_empty_allowlist_grants_all_access(): - """Test that an empty team allowlist grants access to all search tools.""" - mock_team = LiteLLM_TeamTable( - team_id="team-123", - team_alias="Admin Team", - models=["gpt-4"], - allowed_search_tools=[], # Empty = all allowed - ) - - # Should succeed - empty list allows all - result = await can_team_call_search_tool( - search_tool_name="any-search-tool", - team_object=mock_team, - ) - assert result is True - - -@pytest.mark.asyncio -async def test_team_none_allowed_search_tools(): - """Test that None for allowed_search_tools (not set) grants access to all.""" - mock_team = LiteLLM_TeamTable( - team_id="team-123", - team_alias="Legacy Team", - models=["gpt-4"], - allowed_search_tools=None, # Not set = all allowed - ) - - # Should succeed - None allows all - result = await can_team_call_search_tool( - search_tool_name="any-search-tool", - team_object=mock_team, - ) - assert result is True - - -def test_credentials_not_in_team_metadata(): - """Verify that search provider credentials are never stored in team metadata.""" - mock_team = LiteLLM_TeamTable( - team_id="team-123", - team_alias="Test Team", - models=["gpt-4"], - allowed_search_tools=["tavily-search"], - metadata={"custom_field": "value"}, # No search_provider_config - ) - - # Verify metadata does not contain search_provider_config - assert mock_team.metadata is not None - assert "search_provider_config" not in mock_team.metadata - assert "api_key" not in str(mock_team.metadata) - - -def test_credentials_not_in_key_metadata(): - """Verify that search provider credentials are never stored in key metadata.""" - mock_key = UserAPIKeyAuth( - token="sk-test-key", - models=["gpt-4"], - allowed_search_tools=["tavily-search"], - metadata={"user_info": "test"}, # No search_provider_config - ) - - # Verify metadata does not contain search_provider_config - assert mock_key.metadata is not None - assert "search_provider_config" not in mock_key.metadata - assert "api_key" not in str(mock_key.metadata) - - -@pytest.mark.asyncio -async def test_both_key_and_team_checks_required(): - """Test that both key-level and team-level checks are enforced.""" - # Key has access to tool - mock_key = UserAPIKeyAuth( - token="sk-test-key", - models=["gpt-4"], - allowed_search_tools=["tavily-search"], - ) - - # Team does NOT have access to tool - mock_team = LiteLLM_TeamTable( - team_id="team-123", - team_alias="Restricted Team", - models=["gpt-4"], - allowed_search_tools=["perplexity-search"], # Different tool - ) - - # Key check passes - await can_key_call_search_tool( - search_tool_name="tavily-search", - valid_token=mock_key, - ) - - # Team check fails - with pytest.raises(Exception) as exc_info: - await can_team_call_search_tool( - search_tool_name="tavily-search", - team_object=mock_team, - ) - assert "not allowed to access search tool" in str(exc_info.value) From 0543c59af61834237045526e31a44b12bae0f13c Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 28 Apr 2026 18:59:42 +0530 Subject: [PATCH 04/14] revert proxy config --- proxy_server_config.yaml | 232 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 4 deletions(-) diff --git a/proxy_server_config.yaml b/proxy_server_config.yaml index 5a34fd4745..5d3d810926 100644 --- a/proxy_server_config.yaml +++ b/proxy_server_config.yaml @@ -1,7 +1,231 @@ model_list: - # Gemini 2.5 Flash Native Audio (Latest - recommended) - - model_name: gpt-5.3-codex + - model_name: gpt-3.5-turbo-end-user-test litellm_params: - model: openai/gpt-5.3-codex + model: gpt-3.5-turbo + region_name: "eu" + model_info: + id: "1" + - model_name: gpt-3.5-turbo-end-user-test + litellm_params: + model: openai/gpt-4.1-mini + api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault + - model_name: gpt-3.5-turbo + litellm_params: + model: openai/gpt-4.1-mini + api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault + - model_name: gpt-3.5-turbo-large + litellm_params: + model: "gpt-3.5-turbo-1106" api_key: os.environ/OPENAI_API_KEY - \ No newline at end of file + rpm: 480 + timeout: 300 + stream_timeout: 60 + - model_name: gpt-4 + litellm_params: + model: openai/gpt-4.1-mini + api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault + rpm: 480 + timeout: 300 + stream_timeout: 60 + - model_name: sagemaker-completion-model + litellm_params: + model: sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4 + input_cost_per_second: 0.000420 + - model_name: text-embedding-ada-002 + litellm_params: + model: openai/text-embedding-ada-002 + api_key: os.environ/OPENAI_API_KEY + model_info: + mode: embedding + base_model: text-embedding-ada-002 + - model_name: dall-e-2 # some tests use dall-e-2 which is now deprecated, alias to dall-e-3 + litellm_params: + model: openai/dall-e-3 + - model_name: openai-dall-e-3 + litellm_params: + model: dall-e-3 + - model_name: fake-openai-endpoint + litellm_params: + model: openai/gpt-3.5-turbo + api_key: fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + - model_name: fake-openai-endpoint-2 + litellm_params: + model: openai/my-fake-model + api_key: my-fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + stream_timeout: 0.001 + rpm: 1 + - model_name: fake-openai-endpoint-3 + litellm_params: + model: openai/my-fake-model + api_key: my-fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + stream_timeout: 0.001 + rpm: 1000 + - model_name: fake-openai-endpoint-4 + litellm_params: + model: openai/my-fake-model + api_key: my-fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + num_retries: 50 + - model_name: fake-openai-endpoint-3 + litellm_params: + model: openai/my-fake-model-2 + api_key: my-fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + stream_timeout: 0.001 + rpm: 1000 + - model_name: bad-model + litellm_params: + model: openai/bad-model + api_key: os.environ/OPENAI_API_KEY + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + mock_timeout: True + timeout: 60 + rpm: 1000 + model_info: + health_check_timeout: 1 + - model_name: good-model + litellm_params: + model: openai/bad-model + api_key: os.environ/OPENAI_API_KEY + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + rpm: 1000 + model_info: + health_check_timeout: 1 + - model_name: "*" + litellm_params: + model: openai/* + api_key: os.environ/OPENAI_API_KEY + - model_name: realtime-v1 + litellm_params: + model: azure/gpt-realtime-20250828-standard + api_version: "2025-08-28" + realtime_protocol: GA # Possible values: "GA"/ "v1", "beta" + + - model_name: realtime-beta + litellm_params: + model: azure/gpt-realtime-20250828-standard + api_version: 2025-04-01-preview + + + # provider specific wildcard routing + - model_name: "anthropic/*" + litellm_params: + model: "anthropic/*" + api_key: os.environ/ANTHROPIC_API_KEY + - model_name: "bedrock/*" + litellm_params: + model: "bedrock/*" + - model_name: "groq/*" + litellm_params: + model: "groq/*" + api_key: os.environ/GROQ_API_KEY + - model_name: mistral-embed + litellm_params: + model: mistral/mistral-embed + - model_name: gpt-instruct # [PROD TEST] - tests if `/health` automatically infers this to be a text completion model + litellm_params: + model: text-completion-openai/gpt-3.5-turbo-instruct + - model_name: fake-openai-endpoint-5 + litellm_params: + model: openai/my-fake-model + api_key: my-fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + timeout: 1 + - model_name: badly-configured-openai-endpoint + litellm_params: + model: openai/my-fake-model + api_key: my-fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.appxxxx/ + - model_name: gemini-1.5-flash + litellm_params: + model: gemini/gemini-1.5-flash + api_key: os.environ/GOOGLE_API_KEY + - model_name: gpt-4o + litellm_params: + model: gpt-4o + api_key: os.environ/OPENAI_API_KEY + + +litellm_settings: + # set_verbose: True # Uncomment this if you want to see verbose logs; not recommended in production + drop_params: True + success_callback: ["prometheus"] + # max_budget: 100 + # budget_duration: 30d + num_retries: 5 + request_timeout: 600 + telemetry: False + context_window_fallbacks: [{"gpt-3.5-turbo": ["gpt-3.5-turbo-large"]}] + default_team_settings: + - team_id: team-1 + success_callback: ["langfuse"] + failure_callback: ["langfuse"] + langfuse_public_key: os.environ/LANGFUSE_PROJECT1_PUBLIC # Project 1 + langfuse_secret: os.environ/LANGFUSE_PROJECT1_SECRET # Project 1 + - team_id: team-2 + success_callback: ["langfuse"] + failure_callback: ["langfuse"] + langfuse_public_key: os.environ/LANGFUSE_PROJECT2_PUBLIC # Project 2 + langfuse_secret: os.environ/LANGFUSE_PROJECT2_SECRET # Project 2 + langfuse_host: https://us.cloud.langfuse.com + # cache: true # [OPTIONAL] use for caching responses + # enable_caching_on_provider_specific_optional_params: True # Include provider-specific params in cache keys + # cache_params: # And for shared health check + # type: redis + # host: localhost + # port: 6379 + +# For /fine_tuning/jobs endpoints +finetune_settings: + - custom_llm_provider: azure + api_base: os.environ/AZURE_API_BASE + api_key: os.environ/AZURE_API_KEY + api_version: "2023-03-15-preview" + - custom_llm_provider: openai + api_key: os.environ/OPENAI_API_KEY + +# for /files endpoints +files_settings: + - custom_llm_provider: azure + api_base: os.environ/AZURE_API_BASE + api_key: os.environ/AZURE_API_KEY + api_version: "2023-03-15-preview" + - custom_llm_provider: openai + api_key: os.environ/OPENAI_API_KEY + +router_settings: + routing_strategy: usage-based-routing-v2 + redis_host: os.environ/REDIS_HOST + redis_password: os.environ/REDIS_PASSWORD + redis_port: os.environ/REDIS_PORT + enable_pre_call_checks: true + model_group_alias: {"my-special-fake-model-alias-name": "fake-openai-endpoint-3"} + +general_settings: + master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys + store_model_in_db: True + proxy_budget_rescheduler_min_time: 60 + proxy_budget_rescheduler_max_time: 64 + proxy_batch_write_at: 1 + database_connection_pool_limit: 10 + # background_health_checks: true + # use_shared_health_check: true + # health_check_interval: 30 + # database_url: "postgresql://:@:/" # [OPTIONAL] use for token-based auth to proxy + + pass_through_endpoints: + - path: "/v1/rerank" # route you want to add to LiteLLM Proxy Server + target: "https://api.cohere.com/v1/rerank" # URL this route should forward requests to + headers: # headers to forward to this URL + content-type: application/json # (Optional) Extra Headers to pass to this endpoint + accept: application/json + forward_headers: True + +# environment_variables: + # settings for using redis caching + # REDIS_HOST: redis-16337.c322.us-east-1-2.ec2.cloud.redislabs.com + # REDIS_PORT: "16337" + # REDIS_PASSWORD: \ No newline at end of file From 4b03cb68a2f07650aaf66f51fc918888428b44ca Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 12:29:20 +0530 Subject: [PATCH 05/14] feat(proxy): move search tool access to object permissions Store search tool allowlists only on object permissions, wire auth/management/UI flows to object_permission.search_tools, and remove legacy team-metadata search credential code and tests. Made-with: Cursor --- docs/my-website/docs/proxy/team_budgets.md | 58 -------- .../migration.sql | 6 + .../litellm_proxy_extras/schema.prisma | 1 + litellm/proxy/_types.py | 26 +--- litellm/proxy/auth/auth_checks.py | 20 ++- .../key_management_endpoints.py | 9 ++ .../object_permission_utils.py | 45 ++++++ litellm/proxy/schema.prisma | 3 +- litellm/proxy/search_endpoints/endpoints.py | 13 +- litellm/router_utils/search_api_router.py | 55 +------- schema.prisma | 1 + .../test_object_permission_utils.py | 50 +++++++ .../test_team_search_credentials.py | 133 ------------------ ui/litellm-dashboard/next.config.mjs | 5 + .../components/modals/CreateTeamModal.tsx | 99 +++++++------ .../src/components/OldTeams.tsx | 100 ++++++------- .../SearchTools/SearchToolSelector.tsx | 69 +++++++++ .../src/components/molecules/filter.tsx | 1 + .../components/object_permissions_view.tsx | 12 ++ .../src/components/team/TeamInfo.tsx | 67 ++++----- .../src/components/view_logs/index.tsx | 7 +- .../view_logs/log_filter_logic.test.tsx | 46 ++++++ .../components/view_logs/log_filter_logic.tsx | 15 +- 23 files changed, 424 insertions(+), 417 deletions(-) delete mode 100644 docs/my-website/docs/proxy/team_budgets.md create mode 100644 litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql delete mode 100644 tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py create mode 100644 ui/litellm-dashboard/src/components/SearchTools/SearchToolSelector.tsx diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md deleted file mode 100644 index 47f3a832b0..0000000000 --- a/docs/my-website/docs/proxy/team_budgets.md +++ /dev/null @@ -1,58 +0,0 @@ -# Team Budgets and Search Cost Attribution - -When search requests are made through LiteLLM with a team-bound key, spend is attributed to that team. - -## Cost attribution for search - -Search calls (`search` / `asearch`) are logged with: - -- `metadata.user_api_key_team_id` -- spend rows in `LiteLLM_SpendLogs.team_id` - -This means each team's search usage can be queried independently even when using the same model/provider family. - -## Why per-team search keys matter - -Using one shared Tavily key makes upstream provider billing opaque by team. -With team-specific provider keys: - -- provider-side billing is isolated per team -- LiteLLM spend logs still aggregate by team id -- finance can reconcile provider invoices + LiteLLM spend logs - -## Recommended setup - -1. Issue per-team virtual keys in LiteLLM. -2. Configure `metadata.search_provider_config` per team. -3. Keep a fallback tool-level key only for teams without explicit config. - -## Example team update - -```bash -curl -X POST "http://localhost:4000/team/update" \ - -H "Authorization: Bearer sk-admin-key" \ - -H "Content-Type: application/json" \ - -d '{ - "team_id": "team-research", - "metadata": { - "search_provider_config": { - "tavily": { - "api_key": "tvly-research-key" - }, - "perplexity": { - "api_key": "pplx-research-key" - } - } - } - }' -``` - -## Example spend query - -```sql -SELECT team_id, call_type, SUM(spend) AS total_spend, COUNT(*) AS requests -FROM "LiteLLM_SpendLogs" -WHERE call_type IN ('search', 'asearch') -GROUP BY team_id, call_type -ORDER BY total_spend DESC; -``` diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql new file mode 100644 index 0000000000..ebbbf6dcd0 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql @@ -0,0 +1,6 @@ +-- Search tool allowlists live on LiteLLM_ObjectPermissionTable (with agents, MCP, vector stores). +ALTER TABLE "LiteLLM_ObjectPermissionTable" ADD COLUMN IF NOT EXISTS "search_tools" TEXT[] DEFAULT ARRAY[]::TEXT[]; + +-- Unshipped columns: drop if present (e.g. local DBs that had previous Prisma migrate). +ALTER TABLE "LiteLLM_TeamTable" DROP COLUMN IF EXISTS "allowed_search_tools"; +ALTER TABLE "LiteLLM_VerificationToken" DROP COLUMN IF EXISTS "allowed_search_tools"; diff --git a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma index 8f07c5afa3..6c6a2be77b 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma +++ b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma @@ -277,6 +277,7 @@ model LiteLLM_ObjectPermissionTable { models String[] @default([]) blocked_tools String[] @default([]) // Tool names blocked for any key/team/user with this permission mcp_toolsets String[] @default([]) // Toolset IDs granted to this key/team/user + search_tools String[] @default([]) // search_tool_name values this key/team/user may call teams LiteLLM_TeamTable[] projects LiteLLM_ProjectTable[] verification_tokens LiteLLM_VerificationToken[] diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index b0c8f54af4..fd4d4df241 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -904,6 +904,7 @@ class LiteLLM_ObjectPermissionBase(LiteLLMPydanticObjectBase): agents: Optional[List[str]] = None agent_access_groups: Optional[List[str]] = None models: Optional[List[str]] = None + search_tools: Optional[List[str]] = None class BudgetLimitEntry(LiteLLMPydanticObjectBase): @@ -1695,28 +1696,6 @@ class OrgMember(MemberBase): ] -class SearchProviderCredentials(LiteLLMPydanticObjectBase): - """ - Per-team credentials for a search provider. - """ - - api_key: Optional[str] = None - api_base: Optional[str] = None - - -class TeamSearchProviderConfig(LiteLLMPydanticObjectBase): - """ - Structured team-level search provider credentials. - Stored in team metadata under `search_provider_config`. - """ - - tavily: Optional[SearchProviderCredentials] = None - perplexity: Optional[SearchProviderCredentials] = None - brave: Optional[SearchProviderCredentials] = None - exa: Optional[SearchProviderCredentials] = None - serper: Optional[SearchProviderCredentials] = None - - class TeamBase(LiteLLMPydanticObjectBase): team_alias: Optional[str] = None team_id: Optional[str] = None @@ -1738,7 +1717,6 @@ class TeamBase(LiteLLMPydanticObjectBase): ) models: list = [] - allowed_search_tools: list = [] # list of search_tool_name values team can access blocked: bool = False router_settings: Optional[dict] = None access_group_ids: Optional[List[str]] = None @@ -1957,6 +1935,7 @@ class LiteLLM_ObjectPermissionTable(LiteLLMPydanticObjectBase): agent_access_groups: Optional[List[str]] = [] mcp_toolsets: Optional[List[str]] = None blocked_tools: Optional[List[str]] = [] + search_tools: Optional[List[str]] = [] class LiteLLM_TeamTable(TeamBase): @@ -2445,7 +2424,6 @@ class LiteLLM_VerificationToken(LiteLLMPydanticObjectBase): max_budget: Optional[float] = None expires: Optional[Union[str, datetime]] = None models: List = [] - allowed_search_tools: List = [] # list of search_tool_name values key can access aliases: Dict = {} config: Dict = {} user_id: Optional[str] = None diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 7f37783595..0f87bb3506 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -2962,6 +2962,18 @@ async def can_user_call_model( ) +def _search_tool_names_from_object_permission( + object_permission: Optional[LiteLLM_ObjectPermissionTable], +) -> List[str]: + """Return allowlisted search tool names from object_permission (empty = unrestricted).""" + if object_permission is None: + return [] + raw = object_permission.search_tools + if not raw: + return [] + return list(raw) + + def _can_object_call_search_tool( search_tool_name: str, allowed_search_tools: List[str], @@ -3022,7 +3034,9 @@ async def can_key_call_search_tool( """ return _can_object_call_search_tool( search_tool_name=search_tool_name, - allowed_search_tools=valid_token.allowed_search_tools or [], + allowed_search_tools=_search_tool_names_from_object_permission( + valid_token.object_permission + ), object_type="key", ) @@ -3051,7 +3065,9 @@ async def can_team_call_search_tool( return _can_object_call_search_tool( search_tool_name=search_tool_name, - allowed_search_tools=team_object.allowed_search_tools or [], + allowed_search_tools=_search_tool_names_from_object_permission( + team_object.object_permission + ), object_type="team", ) diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index a424f1558f..8129fb0de5 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -65,6 +65,7 @@ from litellm.proxy.management_helpers.object_permission_utils import ( attach_object_permission_to_dict, handle_update_object_permission_common, validate_key_mcp_servers_against_team, + validate_key_search_tools_against_team, ) from litellm.proxy.management_helpers.team_member_permission_checks import ( TeamMemberPermissionChecks, @@ -768,6 +769,10 @@ async def _common_key_generation_helper( # noqa: PLR0915 object_permission=data_json.get("object_permission"), team_obj=team_table, ) + await validate_key_search_tools_against_team( + object_permission=data_json.get("object_permission"), + team_obj=team_table, + ) data_json = await _set_object_permission( data_json=data_json, @@ -2010,6 +2015,10 @@ async def _validate_mcp_servers_for_key_update( object_permission=object_permission_dict, team_obj=effective_team_obj, ) + await validate_key_search_tools_against_team( + object_permission=object_permission_dict, + team_obj=effective_team_obj, + ) async def _validate_update_key_data( diff --git a/litellm/proxy/management_helpers/object_permission_utils.py b/litellm/proxy/management_helpers/object_permission_utils.py index 410f636693..73ba3e97bb 100644 --- a/litellm/proxy/management_helpers/object_permission_utils.py +++ b/litellm/proxy/management_helpers/object_permission_utils.py @@ -400,3 +400,48 @@ async def validate_key_mcp_servers_against_team( ) }, ) + + +def _extract_requested_search_tools(object_permission: Optional[dict]) -> List[str]: + """Return search_tool_name values from a key's object_permission dict.""" + if not object_permission or not isinstance(object_permission, dict): + return [] + raw = object_permission.get("search_tools") + if not isinstance(raw, list): + return [] + return [str(x) for x in raw if x] + + +async def validate_key_search_tools_against_team( + object_permission: Optional[dict], + team_obj: Optional["LiteLLM_TeamTableCachedObj"], +) -> None: + """ + Validate key object_permission.search_tools is a subset of the team's allowlist. + + Empty team allowlist means no restriction at team layer (skip). + """ + requested = _extract_requested_search_tools(object_permission) + if not requested: + return + + team_tools: List[str] = [] + if team_obj is not None and team_obj.object_permission is not None: + st = team_obj.object_permission.search_tools + if st: + team_tools = list(st) + + if not team_tools: + return + + disallowed = set(requested) - set(team_tools) + if disallowed: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail={ + "error": ( + f"Key requests search tools not allowed by team '{team_obj.team_id}': " + f"{sorted(disallowed)}. Team allows: {sorted(team_tools)}." + ) + }, + ) diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 558b4433c2..6c6a2be77b 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -127,7 +127,6 @@ model LiteLLM_TeamTable { soft_budget Float? spend Float @default(0.0) models String[] - allowed_search_tools String[] @default([]) // search_tool_name values team can access max_parallel_requests Int? tpm_limit BigInt? rpm_limit BigInt? @@ -278,6 +277,7 @@ model LiteLLM_ObjectPermissionTable { models String[] @default([]) blocked_tools String[] @default([]) // Tool names blocked for any key/team/user with this permission mcp_toolsets String[] @default([]) // Toolset IDs granted to this key/team/user + search_tools String[] @default([]) // search_tool_name values this key/team/user may call teams LiteLLM_TeamTable[] projects LiteLLM_ProjectTable[] verification_tokens LiteLLM_VerificationToken[] @@ -370,7 +370,6 @@ model LiteLLM_VerificationToken { spend Float @default(0.0) expires DateTime? models String[] - allowed_search_tools String[] @default([]) // search_tool_name values key can access aliases Json @default("{}") config Json @default("{}") router_settings Json? @default("{}") diff --git a/litellm/proxy/search_endpoints/endpoints.py b/litellm/proxy/search_endpoints/endpoints.py index 3d79afc7cf..15ed858b98 100644 --- a/litellm/proxy/search_endpoints/endpoints.py +++ b/litellm/proxy/search_endpoints/endpoints.py @@ -152,11 +152,18 @@ async def search( # Check team-level access if key is associated with a team if user_api_key_dict.team_id: + from litellm.proxy.proxy_server import ( + prisma_client, + proxy_logging_obj, + user_api_key_cache, + ) + team_object = await get_team_object( team_id=user_api_key_dict.team_id, - user_api_key_cache=None, # Will use internal cache - parent_otel_span=None, - proxy_logging_obj=None, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=user_api_key_dict.parent_otel_span, + proxy_logging_obj=proxy_logging_obj, ) await can_team_call_search_tool( search_tool_name=search_tool_name_value, diff --git a/litellm/router_utils/search_api_router.py b/litellm/router_utils/search_api_router.py index 4db337a420..daea3c1370 100644 --- a/litellm/router_utils/search_api_router.py +++ b/litellm/router_utils/search_api_router.py @@ -10,7 +10,6 @@ import traceback from functools import partial from typing import Any, Callable, Dict, Optional, Tuple -import litellm from litellm._logging import verbose_router_logger @@ -21,39 +20,11 @@ class SearchAPIRouter: Provides methods for search tool selection, load balancing, and fallback handling. """ - @staticmethod - def _get_team_config_from_default_settings( - team_id: Optional[str], - ) -> Optional[Dict[str, Any]]: - """ - Resolve team config from litellm.default_team_settings. - - This allows search requests to read per-team settings from proxy config - (YAML) similar to completion paths that use ProxyConfig.load_team_config(). - """ - if not team_id: - return None - - default_team_settings = getattr(litellm, "default_team_settings", None) - if not isinstance(default_team_settings, list): - return None - - for team_setting in default_team_settings: - if ( - isinstance(team_setting, dict) - and team_setting.get("team_id") == team_id - ): - return team_setting - return None - @staticmethod def _resolve_search_provider_credentials( *, search_provider: str, tool_litellm_params: Dict[str, Any], - request_metadata: Optional[Dict[str, Any]] = None, - team_metadata: Optional[Dict[str, Any]] = None, - team_config: Optional[Dict[str, Any]] = None, ) -> Tuple[Optional[str], Optional[str]]: """ Resolve search provider credentials from tool configuration ONLY. @@ -255,37 +226,13 @@ class SearchAPIRouter: f"search_provider not found in litellm_params for search tool '{search_tool_name}'" ) - request_metadata = kwargs.get("metadata") - litellm_metadata = kwargs.get("litellm_metadata") - if not isinstance(request_metadata, dict) and isinstance( - litellm_metadata, dict - ): - request_metadata = litellm_metadata - - team_metadata = {} - team_id: Optional[str] = None - if isinstance(request_metadata, dict): - _team_metadata = request_metadata.get("user_api_key_team_metadata") - if isinstance(_team_metadata, dict): - team_metadata = _team_metadata - _team_id = request_metadata.get("user_api_key_team_id") - if isinstance(_team_id, str): - team_id = _team_id - - team_config = SearchAPIRouter._get_team_config_from_default_settings( - team_id=team_id - ) - api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( search_provider=search_provider, tool_litellm_params=litellm_params, - request_metadata=request_metadata, - team_metadata=team_metadata, - team_config=team_config, ) verbose_router_logger.debug( - f"Selected search tool with provider: {search_provider}, team_id={team_id}" + f"Selected search tool with provider: {search_provider}" ) # Call the original search function with the provider config diff --git a/schema.prisma b/schema.prisma index 8f07c5afa3..6c6a2be77b 100644 --- a/schema.prisma +++ b/schema.prisma @@ -277,6 +277,7 @@ model LiteLLM_ObjectPermissionTable { models String[] @default([]) blocked_tools String[] @default([]) // Tool names blocked for any key/team/user with this permission mcp_toolsets String[] @default([]) // Toolset IDs granted to this key/team/user + search_tools String[] @default([]) // search_tool_name values this key/team/user may call teams LiteLLM_TeamTable[] projects LiteLLM_ProjectTable[] verification_tokens LiteLLM_VerificationToken[] diff --git a/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py b/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py index 1b157a2f6b..b36383dfd9 100644 --- a/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py +++ b/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py @@ -16,6 +16,7 @@ from litellm.proxy.management_helpers.object_permission_utils import ( _resolve_team_allowed_mcp_servers, _set_object_permission, validate_key_mcp_servers_against_team, + validate_key_search_tools_against_team, ) @@ -453,3 +454,52 @@ async def test_resolve_team_allowed_mcp_servers_dict_tool_permissions( result = await _resolve_team_allowed_mcp_servers(mock_perm) assert result == {"server-a"} + + +# ---- Tests for validate_key_search_tools_against_team ---- + + +def _make_team_obj_search(team_id="team-1", search_tools=None): + mock_team = MagicMock() + mock_team.team_id = team_id + if search_tools is not None: + mock_team.object_permission = MagicMock(spec=LiteLLM_ObjectPermissionTable) + mock_team.object_permission.search_tools = search_tools + else: + mock_team.object_permission = None + return mock_team + + +@pytest.mark.asyncio +async def test_validate_search_tools_no_key_request(): + await validate_key_search_tools_against_team( + object_permission=None, + team_obj=_make_team_obj_search(search_tools=["t1"]), + ) + + +@pytest.mark.asyncio +async def test_validate_search_tools_team_unrestricted(): + """Empty team search allowlist means unrestricted — key subset check skipped.""" + await validate_key_search_tools_against_team( + object_permission={"search_tools": ["any-tool"]}, + team_obj=_make_team_obj_search(search_tools=[]), + ) + + +@pytest.mark.asyncio +async def test_validate_search_tools_subset_ok(): + await validate_key_search_tools_against_team( + object_permission={"search_tools": ["t1"]}, + team_obj=_make_team_obj_search(search_tools=["t1", "t2"]), + ) + + +@pytest.mark.asyncio +async def test_validate_search_tools_raises_when_not_subset(): + with pytest.raises(HTTPException) as exc: + await validate_key_search_tools_against_team( + object_permission={"search_tools": ["bad"]}, + team_obj=_make_team_obj_search(search_tools=["t1"]), + ) + assert exc.value.status_code == 403 diff --git a/tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py b/tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py deleted file mode 100644 index eab167fb75..0000000000 --- a/tests/test_litellm/proxy/search_endpoints/test_team_search_credentials.py +++ /dev/null @@ -1,133 +0,0 @@ -import os -import sys -from unittest.mock import patch - -import pytest -from fastapi.testclient import TestClient - -sys.path.insert(0, os.path.abspath("../../../../..")) - -from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth -from litellm.proxy.auth.user_api_key_auth import user_api_key_auth -from litellm.proxy.proxy_server import app -from litellm.router_utils.search_api_router import SearchAPIRouter - - -def test_resolve_credentials_team_metadata_overrides_tool_params(): - api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( - search_provider="tavily", - tool_litellm_params={ - "api_key": "tool-key", - "api_base": "https://tool.example.com", - }, - team_metadata={ - "search_provider_config": { - "tavily": { - "api_key": "team-key", - "api_base": "https://team.example.com", - } - } - }, - ) - assert api_key == "team-key" - assert api_base == "https://team.example.com" - - -def test_resolve_credentials_request_metadata_has_highest_precedence(): - api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( - search_provider="tavily", - tool_litellm_params={ - "api_key": "tool-key", - "api_base": "https://tool.example.com", - }, - request_metadata={ - "search_provider_config": { - "tavily": { - "api_key": "request-key", - "api_base": "https://request.example.com", - } - } - }, - team_metadata={ - "search_provider_config": { - "tavily": { - "api_key": "team-key", - "api_base": "https://team.example.com", - } - } - }, - ) - assert api_key == "request-key" - assert api_base == "https://request.example.com" - - -def test_resolve_credentials_from_default_team_settings(): - with patch( - "litellm.default_team_settings", - [ - { - "team_id": "team-a", - "search_provider_config": { - "tavily": { - "api_key": "team-settings-key", - "api_base": "https://team-settings.example.com", - } - }, - } - ], - ): - team_config = SearchAPIRouter._get_team_config_from_default_settings("team-a") - api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( - search_provider="tavily", - tool_litellm_params={}, - team_config=team_config, - ) - assert api_key == "team-settings-key" - assert api_base == "https://team-settings.example.com" - - -@pytest.mark.asyncio -async def test_search_endpoint_injects_team_metadata(): - captured_metadata = {} - - async def _mock_process(self, **kwargs): - nonlocal captured_metadata - captured_metadata = self.data.get("metadata", {}) - return {"object": "search", "results": []} - - app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( - user_role=LitellmUserRoles.PROXY_ADMIN, - user_id="admin-user", - team_id="team-test", - team_metadata={ - "search_provider_config": { - "tavily": {"api_key": "team-test-key"}, - } - }, - ) - - try: - with patch( - "litellm.proxy.common_request_processing.ProxyBaseLLMRequestProcessing.base_process_llm_request", - new=_mock_process, - ): - client = TestClient(app) - response = client.post( - "/v1/search", - json={ - "search_tool_name": "tool-a", - "search_provider": "tavily", - "query": "latest ai news", - }, - ) - assert response.status_code == 200 - assert captured_metadata.get("user_api_key_team_id") == "team-test" - assert ( - captured_metadata.get("user_api_key_team_metadata", {}) - .get("search_provider_config", {}) - .get("tavily", {}) - .get("api_key") - == "team-test-key" - ) - finally: - app.dependency_overrides.pop(user_api_key_auth, None) diff --git a/ui/litellm-dashboard/next.config.mjs b/ui/litellm-dashboard/next.config.mjs index bdf492de33..cfaeb24dc5 100644 --- a/ui/litellm-dashboard/next.config.mjs +++ b/ui/litellm-dashboard/next.config.mjs @@ -7,6 +7,11 @@ const __dirname = path.dirname(__filename); const nextConfig = { output: "export", + // Required with output: "export" — default image optimizer runs only in server mode. + // See https://nextjs.org/docs/messages/export-image-api + images: { + unoptimized: true, + }, basePath: "", assetPrefix: "/litellm-asset-prefix", turbopack: { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx index 2e14897792..1a8c6632a0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx @@ -17,7 +17,6 @@ import { useQueryClient } from "@tanstack/react-query"; import NotificationsManager from "@/components/molecules/notifications_manager"; import { fetchMCPAccessGroups, - fetchSearchTools, getGuardrailsList, getPoliciesList, Organization, @@ -27,6 +26,7 @@ import { import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { organizationKeys } from "@/app/(dashboard)/hooks/organizations/useOrganizations"; import MCPToolPermissions from "@/components/mcp_server_management/MCPToolPermissions"; +import SearchToolSelector from "@/components/SearchTools/SearchToolSelector"; interface ModelAliases { [key: string]: string; @@ -88,7 +88,6 @@ const CreateTeamModal = ({ const [modelsToPick, setModelsToPick] = useState([]); const [guardrailsList, setGuardrailsList] = useState([]); const [policiesList, setPoliciesList] = useState([]); - const [searchToolNames, setSearchToolNames] = useState([]); const [mcpAccessGroups, setMcpAccessGroups] = useState([]); const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false); @@ -167,24 +166,6 @@ const CreateTeamModal = ({ fetchPolicies(); }, [accessToken]); - useEffect(() => { - const loadSearchTools = async () => { - try { - if (!accessToken) return; - const response = await fetchSearchTools(accessToken); - const tools = Array.isArray(response?.data) ? response.data : []; - setSearchToolNames( - tools - .map((tool: any) => tool?.search_tool_name) - .filter((name: unknown): name is string => typeof name === "string" && name.length > 0), - ); - } catch (error) { - console.error("Failed to fetch search tools for team create modal:", error); - } - }; - loadSearchTools(); - }, [accessToken]); - const handleCreate = async (formValues: Record) => { try { console.log(`formValues: ${JSON.stringify(formValues)}`); @@ -239,15 +220,27 @@ const CreateTeamModal = ({ } } - // Transform allowed_vector_store_ids and allowed_mcp_servers_and_groups into object_permission + // Transform integrations into object_permission (vector stores, MCP, agents, search tools) + const hasAgents = + formValues.allowed_agents_and_groups && + ((formValues.allowed_agents_and_groups.agents?.length ?? 0) > 0 || + (formValues.allowed_agents_and_groups.accessGroups?.length ?? 0) > 0); + const hasSearchTools = + Array.isArray(formValues.object_permission_search_tools) && + formValues.object_permission_search_tools.length > 0; + if ( (formValues.allowed_vector_store_ids && formValues.allowed_vector_store_ids.length > 0) || (formValues.allowed_mcp_servers_and_groups && (formValues.allowed_mcp_servers_and_groups.servers?.length > 0 || formValues.allowed_mcp_servers_and_groups.accessGroups?.length > 0 || - formValues.allowed_mcp_servers_and_groups.toolPermissions)) + formValues.allowed_mcp_servers_and_groups.toolPermissions)) || + hasAgents || + hasSearchTools ) { - formValues.object_permission = {}; + if (!formValues.object_permission) { + formValues.object_permission = {}; + } if (formValues.allowed_vector_store_ids && formValues.allowed_vector_store_ids.length > 0) { formValues.object_permission.vector_stores = formValues.allowed_vector_store_ids; delete formValues.allowed_vector_store_ids; @@ -265,9 +258,6 @@ const CreateTeamModal = ({ // Add tool permissions separately if (formValues.mcp_tool_permissions && Object.keys(formValues.mcp_tool_permissions).length > 0) { - if (!formValues.object_permission) { - formValues.object_permission = {}; - } formValues.object_permission.mcp_tool_permissions = formValues.mcp_tool_permissions; delete formValues.mcp_tool_permissions; } @@ -275,9 +265,6 @@ const CreateTeamModal = ({ // Handle agent permissions if (formValues.allowed_agents_and_groups) { const { agents, accessGroups } = formValues.allowed_agents_and_groups; - if (!formValues.object_permission) { - formValues.object_permission = {}; - } if (agents && agents.length > 0) { formValues.object_permission.agents = agents; } @@ -286,6 +273,11 @@ const CreateTeamModal = ({ } delete formValues.allowed_agents_and_groups; } + + if (hasSearchTools) { + formValues.object_permission.search_tools = formValues.object_permission_search_tools; + delete formValues.object_permission_search_tools; + } } // Transform allowed_mcp_access_groups into object_permission @@ -422,27 +414,6 @@ const CreateTeamModal = ({
- - Allowed Search Tools{" "} - - - - - } - name="allowed_search_tools" - > - ({ label: name, value: name }))} - showSearch - optionFilterProp="label" - /> - - Team Member Settings @@ -777,6 +748,34 @@ const CreateTeamModal = ({ + + + Search Tool Settings + + + + Allowed Search Tools{" "} + + + + + } + name="object_permission_search_tools" + className="mt-4" + help="Restrict which configured search tools keys on this team may call." + > + form.setFieldValue("object_permission_search_tools", vals)} + value={form.getFieldValue("object_permission_search_tools")} + accessToken={accessToken || ""} + placeholder="Select search tools (optional, empty = all allowed)" + /> + + + + Logging Settings diff --git a/ui/litellm-dashboard/src/components/OldTeams.tsx b/ui/litellm-dashboard/src/components/OldTeams.tsx index c7f39c0dcd..4edc1bd044 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.tsx @@ -60,13 +60,13 @@ import NotificationsManager from "./molecules/notifications_manager"; import { Organization, fetchMCPAccessGroups, - fetchSearchTools, getGuardrailsList, getPoliciesList, teamDeleteCall, } from "./networking"; import NumericalInput from "./shared/numerical_input"; import VectorStoreSelector from "./vector_store_management/VectorStoreSelector"; +import SearchToolSelector from "./SearchTools/SearchToolSelector"; interface TeamProps { teams: Team[] | null; @@ -274,7 +274,6 @@ const Teams: React.FC = ({ // Add this state near the other useState declarations const [guardrailsList, setGuardrailsList] = useState([]); const [policiesList, setPoliciesList] = useState([]); - const [searchToolNames, setSearchToolNames] = useState([]); const [loggingSettings, setLoggingSettings] = useState([]); const [mcpAccessGroups, setMcpAccessGroups] = useState([]); const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false); @@ -342,24 +341,6 @@ const Teams: React.FC = ({ fetchPolicies(); }, [accessToken]); - useEffect(() => { - const loadSearchTools = async () => { - try { - if (!accessToken) return; - const response = await fetchSearchTools(accessToken); - const tools = Array.isArray(response?.data) ? response.data : []; - setSearchToolNames( - tools - .map((tool: any) => tool?.search_tool_name) - .filter((name: unknown): name is string => typeof name === "string" && name.length > 0), - ); - } catch (error) { - console.error("Failed to fetch search tools:", error); - } - }; - loadSearchTools(); - }, [accessToken]); - const fetchMcpAccessGroups = async () => { try { if (accessToken == null) { @@ -531,15 +512,27 @@ const Teams: React.FC = ({ } } - // Transform allowed_vector_store_ids and allowed_mcp_servers_and_groups into object_permission + // Transform integrations into object_permission + const hasAgents = + formValues.allowed_agents_and_groups && + ((formValues.allowed_agents_and_groups.agents?.length ?? 0) > 0 || + (formValues.allowed_agents_and_groups.accessGroups?.length ?? 0) > 0); + const hasSearchTools = + Array.isArray(formValues.object_permission_search_tools) && + formValues.object_permission_search_tools.length > 0; + if ( (formValues.allowed_vector_store_ids && formValues.allowed_vector_store_ids.length > 0) || (formValues.allowed_mcp_servers_and_groups && (formValues.allowed_mcp_servers_and_groups.servers?.length > 0 || formValues.allowed_mcp_servers_and_groups.accessGroups?.length > 0 || - formValues.allowed_mcp_servers_and_groups.toolPermissions)) + formValues.allowed_mcp_servers_and_groups.toolPermissions)) || + hasAgents || + hasSearchTools ) { - formValues.object_permission = {}; + if (!formValues.object_permission) { + formValues.object_permission = {}; + } if (formValues.allowed_vector_store_ids && formValues.allowed_vector_store_ids.length > 0) { formValues.object_permission.vector_stores = formValues.allowed_vector_store_ids; delete formValues.allowed_vector_store_ids; @@ -555,11 +548,7 @@ const Teams: React.FC = ({ delete formValues.allowed_mcp_servers_and_groups; } - // Add tool permissions separately if (formValues.mcp_tool_permissions && Object.keys(formValues.mcp_tool_permissions).length > 0) { - if (!formValues.object_permission) { - formValues.object_permission = {}; - } formValues.object_permission.mcp_tool_permissions = formValues.mcp_tool_permissions; delete formValues.mcp_tool_permissions; } @@ -589,6 +578,14 @@ const Teams: React.FC = ({ delete formValues.allowed_agents_and_groups; } + if (hasSearchTools) { + if (!formValues.object_permission) { + formValues.object_permission = {}; + } + formValues.object_permission.search_tools = formValues.object_permission_search_tools; + delete formValues.object_permission_search_tools; + } + // Add model_aliases if any are defined if (Object.keys(modelAliases).length > 0) { formValues.model_aliases = modelAliases; @@ -1233,27 +1230,6 @@ const Teams: React.FC = ({ /> - - Allowed Search Tools{" "} - - - - - } - name="allowed_search_tools" - > - + ); +}; + +export default SearchToolSelector; diff --git a/ui/litellm-dashboard/src/components/molecules/filter.tsx b/ui/litellm-dashboard/src/components/molecules/filter.tsx index 34ff1983f3..29a55c1f03 100644 --- a/ui/litellm-dashboard/src/components/molecules/filter.tsx +++ b/ui/litellm-dashboard/src/components/molecules/filter.tsx @@ -141,6 +141,7 @@ const FilterComponent: React.FC = ({ "Error Message", "Key Hash", "Model", + "Public model / search tool", ]; return ( diff --git a/ui/litellm-dashboard/src/components/object_permissions_view.tsx b/ui/litellm-dashboard/src/components/object_permissions_view.tsx index 685467e1d3..92aa8679a4 100644 --- a/ui/litellm-dashboard/src/components/object_permissions_view.tsx +++ b/ui/litellm-dashboard/src/components/object_permissions_view.tsx @@ -13,6 +13,7 @@ interface ObjectPermission { vector_stores: string[]; agents?: string[]; agent_access_groups?: string[]; + search_tools?: string[]; } interface ObjectPermissionsViewProps { @@ -35,6 +36,7 @@ export function ObjectPermissionsView({ const mcpToolsets = objectPermission?.mcp_toolsets || []; const agents = objectPermission?.agents || []; const agentAccessGroups = objectPermission?.agent_access_groups || []; + const searchTools = objectPermission?.search_tools || []; const content = (
@@ -51,6 +53,16 @@ export function ObjectPermissionsView({ agentAccessGroups={agentAccessGroups} accessToken={accessToken} /> +
+ Search tools + {searchTools.length === 0 ? ( + + No restriction — all configured search tools are allowed for this team. + + ) : ( + {searchTools.join(", ")} + )} +
); diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index 6cdc4fc3a7..d2c00e5326 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -3,7 +3,6 @@ import { organizationKeys, useOrganizations } from "@/app/(dashboard)/hooks/orga import { useQueryClient } from "@tanstack/react-query"; import UserSearchModal from "@/components/common_components/user_search_modal"; import { - fetchSearchTools, getPoliciesList, getPolicyInfoWithGuardrails, Member, @@ -43,6 +42,7 @@ import { fetchMCPAccessGroups } from "../networking"; import ObjectPermissionsView from "../object_permissions_view"; import NumericalInput from "../shared/numerical_input"; import VectorStoreSelector from "../vector_store_management/VectorStoreSelector"; +import SearchToolSelector from "../SearchTools/SearchToolSelector"; import EditLoggingSettings from "./EditLoggingSettings"; import RouterSettingsAccordion, { RouterSettingsAccordionRef } from "../common_components/RouterSettingsAccordion"; import MemberModal from "./EditMembership"; @@ -103,7 +103,6 @@ export interface TeamData { } | null; created_at: string; access_group_ids?: string[]; - allowed_search_tools?: string[]; default_team_member_models?: string[]; access_group_models?: string[]; access_group_mcp_server_ids?: string[]; @@ -120,6 +119,7 @@ export interface TeamData { vector_stores: string[]; agents?: string[]; agent_access_groups?: string[]; + search_tools?: string[]; }; team_member_budget_table: { max_budget: number; @@ -193,7 +193,6 @@ const TeamInfoView: React.FC = ({ const { data: guardrailsData, isLoading: isGuardrailsLoading } = useGuardrails(); const globalGuardrailNames = guardrailsData?.globalGuardrailNames ?? new Set(); const [policiesList, setPoliciesList] = useState([]); - const [searchToolNames, setSearchToolNames] = useState([]); const [policyGuardrails, setPolicyGuardrails] = useState>({}); const [loadingPolicies, setLoadingPolicies] = useState(false); const [memberToDelete, setMemberToDelete] = useState(null); @@ -303,24 +302,6 @@ const TeamInfoView: React.FC = ({ fetchPolicies(); }, [accessToken]); - useEffect(() => { - const loadSearchTools = async () => { - try { - if (!accessToken) return; - const response = await fetchSearchTools(accessToken); - const tools = Array.isArray(response?.data) ? response.data : []; - setSearchToolNames( - tools - .map((tool: any) => tool?.search_tool_name) - .filter((name: unknown): name is string => typeof name === "string" && name.length > 0), - ); - } catch (error) { - console.error("Failed to fetch search tools in team info:", error); - } - }; - loadSearchTools(); - }, [accessToken]); - // Fetch resolved guardrails for all policies useEffect(() => { const fetchPolicyGuardrails = async () => { @@ -525,7 +506,6 @@ const TeamInfoView: React.FC = ({ team_id: teamId, team_alias: values.team_alias, models: values.models, - allowed_search_tools: values.allowed_search_tools || [], tpm_limit: sanitizeNumeric(values.tpm_limit), rpm_limit: sanitizeNumeric(values.rpm_limit), model_tpm_limit: modelTpmLimit, @@ -615,6 +595,10 @@ const TeamInfoView: React.FC = ({ updateData.object_permission.vector_stores = values.vector_stores; } + if (Array.isArray(values.object_permission_search_tools)) { + updateData.object_permission.search_tools = values.object_permission_search_tools; + } + // Pass access_group_ids to the update request if (values.access_group_ids !== undefined) { updateData.access_group_ids = values.access_group_ids; @@ -948,7 +932,7 @@ const TeamInfoView: React.FC = ({ models: info.models, tpm_limit: info.tpm_limit, rpm_limit: info.rpm_limit, - allowed_search_tools: info.allowed_search_tools || [], + object_permission_search_tools: info.object_permission?.search_tools || [], modelLimits: Array.from( new Set([ ...Object.keys(info.metadata?.model_tpm_limit ?? {}), @@ -1031,23 +1015,6 @@ const TeamInfoView: React.FC = ({ />
- - { expect(filters["Key Alias"]).toBe(""); expect(filters["Error Code"]).toBe(""); expect(filters["Error Message"]).toBe(""); + expect(filters["Public model / search tool"]).toBe(""); }); it("should return all logs when no filters are applied", () => { @@ -200,6 +201,51 @@ describe("useLogFilterLogic", () => { ); }); + it("should pass model param and filter search-tool rows by spend log model column", async () => { + const searchRows = [ + createLogEntry({ + request_id: "s1", + call_type: "asearch", + model: "tavily-marketing", + model_id: "", + team_id: "team-x", + }), + ]; + vi.mocked(uiSpendLogsCall).mockResolvedValue(createPaginatedResponse(searchRows)); + const logs = createPaginatedResponse([ + ...searchRows, + createLogEntry({ + request_id: "c1", + call_type: "chat", + model: "gpt-4o", + model_id: "mid-1", + team_id: "team-x", + }), + ]); + const { result } = renderHook(() => useLogFilterLogic({ ...defaultProps, logs }), { wrapper }); + + act(() => { + result.current.handleFilterChange({ "Public model / search tool": "tavily-marketing" }); + }); + + await waitFor( + () => { + expect(result.current.filteredLogs.data).toHaveLength(1); + expect(result.current.filteredLogs.data[0].model).toBe("tavily-marketing"); + expect(result.current.filteredLogs.data[0].call_type).toBe("asearch"); + }, + { timeout: 500 }, + ); + + expect(vi.mocked(uiSpendLogsCall)).toHaveBeenCalledWith( + expect.objectContaining({ + params: expect.objectContaining({ + model: "tavily-marketing", + }), + }), + ); + }); + it("should filter logs by api_key when Key Hash filter is set", async () => { const filteredLog = createLogEntry({ request_id: "req-1", api_key: "key-x" }); vi.mocked(uiSpendLogsCall).mockResolvedValue( diff --git a/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx b/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx index a538872bfd..8f916999c1 100644 --- a/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx @@ -9,11 +9,14 @@ import { defaultPageSize } from "../constants"; import { PaginatedResponse } from "."; import type { LogsSortField } from "./columns"; -const FILTER_KEYS = { +/** Spend log `model` column (LLM public model name or `search_tool_name` for /search). */ +export const FILTER_KEYS = { TEAM_ID: "Team ID", KEY_HASH: "Key Hash", REQUEST_ID: "Request ID", MODEL: "Model", + /** Exact match on LiteLLM_SpendLogs.model — use for search tools and public model names. */ + PUBLIC_MODEL_OR_SEARCH_TOOL: "Public model / search tool", USER_ID: "User ID", END_USER: "End User", STATUS: "Status", @@ -58,6 +61,7 @@ export function useLogFilterLogic({ [FILTER_KEYS.KEY_HASH]: "", [FILTER_KEYS.REQUEST_ID]: "", [FILTER_KEYS.MODEL]: "", + [FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL]: "", [FILTER_KEYS.USER_ID]: "", [FILTER_KEYS.END_USER]: "", [FILTER_KEYS.STATUS]: "", @@ -107,6 +111,7 @@ export function useLogFilterLogic({ end_user: filters[FILTER_KEYS.END_USER] || undefined, status_filter: filters[FILTER_KEYS.STATUS] || undefined, model_id: filters[FILTER_KEYS.MODEL] || undefined, + model: filters[FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL] || undefined, key_alias: filters[FILTER_KEYS.KEY_ALIAS] || undefined, error_code: filters[FILTER_KEYS.ERROR_CODE] || undefined, error_message: filters[FILTER_KEYS.ERROR_MESSAGE] || undefined, @@ -155,7 +160,8 @@ export function useLogFilterLogic({ filters[FILTER_KEYS.END_USER] || filters[FILTER_KEYS.ERROR_CODE] || filters[FILTER_KEYS.ERROR_MESSAGE] || - filters[FILTER_KEYS.MODEL] + filters[FILTER_KEYS.MODEL] || + filters[FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL] ), [filters], ); @@ -218,6 +224,11 @@ export function useLogFilterLogic({ filteredData = filteredData.filter((log) => log.model_id === filters[FILTER_KEYS.MODEL]); } + if (filters[FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL]) { + const m = filters[FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL]; + filteredData = filteredData.filter((log) => log.model === m); + } + if (filters[FILTER_KEYS.KEY_HASH]) { filteredData = filteredData.filter((log) => log.api_key === filters[FILTER_KEYS.KEY_HASH]); } From 814e785ffb7b680cb3e808beb0ef7b4538c61cae Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 12:29:56 +0530 Subject: [PATCH 06/14] Remove unused docs --- docs/my-website/docs/proxy/search.md | 92 ---- .../docs/proxy/search_tools_access.md | 439 ------------------ 2 files changed, 531 deletions(-) delete mode 100644 docs/my-website/docs/proxy/search.md delete mode 100644 docs/my-website/docs/proxy/search_tools_access.md diff --git a/docs/my-website/docs/proxy/search.md b/docs/my-website/docs/proxy/search.md deleted file mode 100644 index 65c431eb53..0000000000 --- a/docs/my-website/docs/proxy/search.md +++ /dev/null @@ -1,92 +0,0 @@ -# Search API - -LiteLLM supports team-aware search provider credentials for providers like Tavily, Perplexity, Brave, Exa, and Serper. - -## Per-team search provider configuration - -Set per-team credentials in team metadata: - -```json -{ - "search_provider_config": { - "tavily": { - "api_key": "tvly-team-a-key", - "api_base": "https://api.tavily.com" - }, - "perplexity": { - "api_key": "pplx-team-a-key" - } - } -} -``` - -Update via API: - -```bash -curl -X POST "http://localhost:4000/team/search_provider_config/update" \ - -H "Authorization: Bearer sk-admin-key" \ - -H "Content-Type: application/json" \ - -d '{ - "team_id": "team-a", - "provider": "tavily", - "api_key": "tvly-team-a-key", - "api_base": "https://api.tavily.com" - }' -``` - -## Request flow and precedence - -Search credentials resolve in this order: - -1. Request metadata: `metadata.search_provider_config.` -2. Team DB metadata: `user_api_key_team_metadata.search_provider_config.` -3. YAML team settings: `default_team_settings[].search_provider_config.` -4. Search tool config: `search_tools[].litellm_params` -5. Provider env fallback (`TAVILY_API_KEY`, etc.) - -## Calling search as an end-user - -The caller only uses their team-bound virtual key. - -```bash -curl -X POST "http://localhost:4000/v1/search" \ - -H "Authorization: Bearer sk-team-a-user-key" \ - -H "Content-Type: application/json" \ - -d '{ - "search_tool_name": "company-search", - "query": "latest AI news", - "max_results": 5 - }' -``` - -or with URL tool name: - -```bash -curl -X POST "http://localhost:4000/v1/search/company-search" \ - -H "Authorization: Bearer sk-team-a-user-key" \ - -H "Content-Type: application/json" \ - -d '{ - "query": "latest AI news", - "max_results": 5 - }' -``` - -## YAML examples - -```yaml -search_tools: - - search_tool_name: company-search - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_DEFAULT_API_KEY - -default_team_settings: - - team_id: team-a - search_provider_config: - tavily: - api_key: os.environ/TAVILY_TEAM_A_API_KEY - - team_id: team-b - search_provider_config: - tavily: - api_key: os.environ/TAVILY_TEAM_B_API_KEY -``` diff --git a/docs/my-website/docs/proxy/search_tools_access.md b/docs/my-website/docs/proxy/search_tools_access.md deleted file mode 100644 index 8c04735363..0000000000 --- a/docs/my-website/docs/proxy/search_tools_access.md +++ /dev/null @@ -1,439 +0,0 @@ -# Search Tools Access Control - -Control which teams and keys can access specific search tools using model-like allowlists. - -## Overview - -Search tools in LiteLLM Proxy use the same access control pattern as models: - -- **Team-level allowlist**: `allowed_search_tools` on teams -- **Key-level allowlist**: `allowed_search_tools` on keys -- **Tool-only credentials**: API keys stored ONLY in search tool configuration -- **Secure by default**: Credentials never exposed in team/key metadata - -## Quick Start - -### Step 1: Configure Search Tools - -Define search tools in your `proxy_server_config.yaml`: - -```yaml -search_tools: - - search_tool_name: perplexity-search - litellm_params: - search_provider: perplexity - api_key: os.environ/PERPLEXITYAI_API_KEY - - - search_tool_name: tavily-search - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_API_KEY - - - search_tool_name: tavily-marketing - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_MARKETING_API_KEY - - - search_tool_name: brave-search - litellm_params: - search_provider: brave - api_key: os.environ/BRAVE_API_KEY -``` - -### Step 2: Create Teams with Search Tool Access - -```bash -curl -X POST 'http://localhost:4000/team/new' \ - -H 'Authorization: Bearer ' \ - -H 'Content-Type: application/json' \ - -d '{ - "team_alias": "marketing-team", - "models": ["gpt-4"], - "allowed_search_tools": ["tavily-marketing", "perplexity-search"] - }' -``` - -### Step 3: Generate Keys for Teams - -```bash -curl -X POST 'http://localhost:4000/key/generate' \ - -H 'Authorization: Bearer ' \ - -H 'Content-Type: application/json' \ - -d '{ - "team_id": "", - "models": ["gpt-4"], - "allowed_search_tools": ["tavily-marketing"] - }' -``` - -### Step 4: Use Search Tools - -```bash -curl -X POST 'http://localhost:4000/v1/search/tavily-marketing' \ - -H 'Authorization: Bearer sk-...' \ - -d '{"query": "latest marketing trends"}' -``` - -## Access Control Rules - -### Authorization Flow - -```mermaid -flowchart TD - Request["/v1/search/tavily-search"] --> KeyCheck{Key has access?} - KeyCheck -->|No| Deny403[403 Forbidden] - KeyCheck -->|Yes| TeamCheck{Team has access?} - TeamCheck -->|No| Deny403 - TeamCheck -->|Yes| GetCreds[Get credentials from tool config] - GetCreds --> CallAPI[Call Tavily API] -``` - -### Allowlist Behavior - -| Allowlist Value | Behavior | -|----------------|----------| -| `[]` (empty) | Access to **all** search tools | -| `["tool-a", "tool-b"]` | Access only to `tool-a` and `tool-b` | -| Not set / `null` | Access to **all** search tools | - -### Examples - -**Example 1: Team restricts tools, key further restricts** - -```yaml -# Team allows 3 tools -team.allowed_search_tools = ["tavily", "perplexity", "brave"] - -# Key only allows 1 tool -key.allowed_search_tools = ["tavily"] - -# Result: Key can ONLY access "tavily" -``` - -**Example 2: Empty allowlists grant full access** - -```yaml -# Team allows all -team.allowed_search_tools = [] - -# Key allows all -key.allowed_search_tools = [] - -# Result: Key can access ANY search tool -``` - -**Example 3: Team blocks access even if key allows** - -```yaml -# Team restricts to perplexity -team.allowed_search_tools = ["perplexity"] - -# Key allows tavily -key.allowed_search_tools = ["tavily"] - -# Result: Access DENIED - team doesn't allow tavily -``` - -## Configuration Patterns - -### Pattern 1: Per-Team Search Tool Isolation - -Each team gets their own search tool with dedicated credentials: - -```yaml -search_tools: - - search_tool_name: tavily-team-a - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_TEAM_A_KEY - - - search_tool_name: tavily-team-b - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_TEAM_B_KEY -``` - -```bash -# Create teams with isolated tools -curl -X POST 'http://localhost:4000/team/new' \ - -H 'Authorization: Bearer ' \ - -d '{ - "team_alias": "team-a", - "allowed_search_tools": ["tavily-team-a"] - }' -``` - -**Benefits**: -- Complete cost isolation (different Tavily accounts) -- Separate rate limits per team -- Independent billing - -### Pattern 2: Shared Tools with Access Control - -Share search tools across teams with allowlist restrictions: - -```yaml -search_tools: - - search_tool_name: tavily-premium - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_PREMIUM_KEY - - - search_tool_name: perplexity-standard - litellm_params: - search_provider: perplexity - api_key: os.environ/PERPLEXITY_KEY -``` - -```bash -# Enterprise team gets premium tools -curl -X POST 'http://localhost:4000/team/new' \ - -d '{ - "team_alias": "enterprise", - "allowed_search_tools": ["tavily-premium", "perplexity-standard"] - }' - -# Regular team gets standard tools only -curl -X POST 'http://localhost:4000/team/new' \ - -d '{ - "team_alias": "standard", - "allowed_search_tools": ["perplexity-standard"] - }' -``` - -### Pattern 3: Open Access with Cost Tracking - -Allow all teams to access tools, track costs via `team_id`: - -```yaml -search_tools: - - search_tool_name: tavily-shared - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_SHARED_KEY -``` - -```bash -# Teams with empty allowlists can access all tools -curl -X POST 'http://localhost:4000/team/new' \ - -d '{ - "team_alias": "team-a", - "allowed_search_tools": [] - }' -``` - -Query spend by team: - -```sql -SELECT - team_id, - SUM(spend) as total_spend, - COUNT(*) as request_count -FROM "LiteLLM_SpendLogs" -WHERE call_type = 'search' - AND model LIKE 'tavily%' -GROUP BY team_id; -``` - -## Security Model - -### Credentials Storage - -**Secure**: Credentials stored ONLY in search tool configuration - -```yaml -# ✅ CORRECT - Credentials in tool config -search_tools: - - search_tool_name: tavily-search - litellm_params: - api_key: os.environ/TAVILY_API_KEY # Stored here -``` - -**Never in team/key metadata**: - -```json -{ - "team_id": "team-123", - "allowed_search_tools": ["tavily-search"], - "metadata": {} // ✅ No credentials here -} -``` - -### Access Control Only - -Teams and keys only specify **which tools** they can access, not credentials: - -```json -{ - "team": { - "allowed_search_tools": ["tool-a", "tool-b"] // Access control - }, - "key": { - "allowed_search_tools": ["tool-a"] // Access control - } -} -``` - -## API Reference - -### Create Team with Search Tools - -```bash -POST /team/new - -{ - "team_alias": "marketing", - "models": ["gpt-4"], - "allowed_search_tools": ["tavily-search", "perplexity-search"] -} -``` - -### Update Team Search Tools - -```bash -POST /team/update - -{ - "team_id": "team-123", - "allowed_search_tools": ["brave-search"] -} -``` - -### Generate Key with Search Tools - -```bash -POST /key/generate - -{ - "team_id": "team-123", - "models": ["gpt-4"], - "allowed_search_tools": ["tavily-search"] -} -``` - -### List Available Search Tools - -```bash -GET /v1/search/tools - -# Response: -{ - "object": "list", - "data": [ - { - "search_tool_name": "tavily-search", - "search_provider": "tavily" - } - ] -} -``` - -## Cost Attribution - -Search requests are automatically attributed to the team via `team_id` in spend logs: - -```sql -SELECT - team_id, - model as search_tool, - SUM(spend) as cost, - COUNT(*) as requests -FROM "LiteLLM_SpendLogs" -WHERE call_type = 'search' - AND created_at >= NOW() - INTERVAL '30 days' -GROUP BY team_id, model -ORDER BY cost DESC; -``` - -**Example output**: - -| team_id | search_tool | cost | requests | -|---------|-------------|------|----------| -| team-marketing | tavily-search | $45.20 | 904 | -| team-engineering | perplexity-search | $32.15 | 643 | -| team-research | brave-search | $8.50 | 170 | - -## Migration from Legacy Approach - -If you previously stored credentials in team metadata, migrate to the new approach: - -### Before (Insecure) - -```json -{ - "team": { - "metadata": { - "search_provider_config": { - "tavily": {"api_key": "tvly-..."} // ❌ Exposed - } - } - } -} -``` - -### After (Secure) - -```yaml -# 1. Move credentials to search tool config -search_tools: - - search_tool_name: tavily-marketing - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_MARKETING_KEY # ✅ Secure - -# 2. Update team with allowlist -team: - allowed_search_tools: ["tavily-marketing"] # ✅ Access control only -``` - -## Troubleshooting - -### 403 Forbidden Error - -```json -{ - "error": "Key not allowed to access search tool: tavily-search. - Allowed search tools: [perplexity-search]" -} -``` - -**Solution**: Add the search tool to key's `allowed_search_tools`: - -```bash -curl -X POST 'http://localhost:4000/key/update' \ - -d '{ - "key": "sk-...", - "allowed_search_tools": ["tavily-search", "perplexity-search"] - }' -``` - -### Search Tool Not Found - -```json -{"error": "Search tool not found: tavily-search"} -``` - -**Solution**: Add the search tool to your `proxy_server_config.yaml`: - -```yaml -search_tools: - - search_tool_name: tavily-search - litellm_params: - search_provider: tavily - api_key: os.environ/TAVILY_API_KEY -``` - -## Best Practices - -1. **Use descriptive tool names**: `tavily-marketing` vs `tavily-1` -2. **Empty allowlists for admins**: Grant full access to admin teams -3. **Restrict by role**: Marketing gets marketing tools, engineering gets code search -4. **Monitor costs per team**: Query spend logs regularly -5. **Rotate credentials in tools**: Update environment variables, not team metadata -6. **Start restrictive**: Add tools to allowlists as needed - -## Related - -- [Search API Reference](./search.md) -- [Team Management](./team_budgets.md) -- [Cost Tracking](./cost_tracking.md) From b5c60d88737de692507d55eecbd38d4bf46e0a95 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 12:30:10 +0530 Subject: [PATCH 07/14] Add migration script --- .../migration.sql | 4 ---- 1 file changed, 4 deletions(-) diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql index ebbbf6dcd0..bffdaaebc5 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260429120000_search_tools_on_object_permission/migration.sql @@ -1,6 +1,2 @@ -- Search tool allowlists live on LiteLLM_ObjectPermissionTable (with agents, MCP, vector stores). ALTER TABLE "LiteLLM_ObjectPermissionTable" ADD COLUMN IF NOT EXISTS "search_tools" TEXT[] DEFAULT ARRAY[]::TEXT[]; - --- Unshipped columns: drop if present (e.g. local DBs that had previous Prisma migrate). -ALTER TABLE "LiteLLM_TeamTable" DROP COLUMN IF EXISTS "allowed_search_tools"; -ALTER TABLE "LiteLLM_VerificationToken" DROP COLUMN IF EXISTS "allowed_search_tools"; From 7b307c4298e2dcaa112ed8b78ad88b2e05f98603 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 12:36:50 +0530 Subject: [PATCH 08/14] fix lint --- litellm/proxy/auth/auth_checks.py | 30 +++++++++++------------ litellm/router_utils/search_api_router.py | 6 ++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 0f87bb3506..a9acc1f2c5 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -2981,28 +2981,28 @@ def _can_object_call_search_tool( ) -> Literal[True]: """ Check if an object (key/team/project) can access a specific search tool. - + Similar to _can_object_call_model but for search tools. - + Args: search_tool_name: The search tool being requested allowed_search_tools: List of allowed search tool names for this object object_type: Type of object for error messaging - + Returns: True if access is allowed - + Raises: ProxyException if access is denied """ # Empty list means all search tools are allowed if not allowed_search_tools: return True - + # Check if the search tool is in the allowlist if search_tool_name in allowed_search_tools: return True - + # Access denied raise ProxyException( message=f"{object_type.capitalize()} not allowed to access search tool: {search_tool_name}. " @@ -3019,16 +3019,16 @@ async def can_key_call_search_tool( ) -> Literal[True]: """ Check if a key can access a specific search tool. - + Similar to can_key_call_model but for search tools. - + Args: search_tool_name: The search tool being requested valid_token: The authenticated key - + Returns: True if access is allowed - + Raises: ProxyException if access is denied """ @@ -3047,22 +3047,22 @@ async def can_team_call_search_tool( ) -> Literal[True]: """ Check if a team can access a specific search tool. - + Similar to can_team_access_model but for search tools. - + Args: search_tool_name: The search tool being requested team_object: The team object - + Returns: True if access is allowed - + Raises: ProxyException if access is denied """ if team_object is None: return True - + return _can_object_call_search_tool( search_tool_name=search_tool_name, allowed_search_tools=_search_tool_names_from_object_permission( diff --git a/litellm/router_utils/search_api_router.py b/litellm/router_utils/search_api_router.py index daea3c1370..dc9cafceef 100644 --- a/litellm/router_utils/search_api_router.py +++ b/litellm/router_utils/search_api_router.py @@ -28,13 +28,13 @@ class SearchAPIRouter: ) -> Tuple[Optional[str], Optional[str]]: """ Resolve search provider credentials from tool configuration ONLY. - + Credentials are stored only in search_tool.litellm_params, never in team/key metadata. This ensures secrets are not exposed in team/key API responses. - + Args: tool_litellm_params: Search tool litellm_params with credentials - + Returns: Tuple of (api_key, api_base) from tool configuration """ From d592dc5840a1175a5032c92781030bb5f99c3956 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 12:50:40 +0530 Subject: [PATCH 09/14] Fix mypy --- .../object_permission_utils.py | 54 +++---------------- 1 file changed, 6 insertions(+), 48 deletions(-) diff --git a/litellm/proxy/management_helpers/object_permission_utils.py b/litellm/proxy/management_helpers/object_permission_utils.py index 73ba3e97bb..552478f7b5 100644 --- a/litellm/proxy/management_helpers/object_permission_utils.py +++ b/litellm/proxy/management_helpers/object_permission_utils.py @@ -335,8 +335,9 @@ async def validate_key_mcp_servers_against_team( disallowed_servers = requested_servers - all_allowed_servers if disallowed_servers: if team_obj is not None: + team_id = team_obj.team_id detail = ( - f"Key requests MCP servers not allowed by team '{team_obj.team_id}': " + f"Key requests MCP servers not allowed by team '{team_id}': " f"{sorted(disallowed_servers)}. " f"Team allows: {sorted(team_allowed_servers)}. " f"Global (allow_all_keys) servers: {sorted(allow_all_keys_servers)}." @@ -365,8 +366,9 @@ async def validate_key_mcp_servers_against_team( disallowed_groups = requested_access_groups - team_access_groups if disallowed_groups: if team_obj is not None: + team_id = team_obj.team_id detail = ( - f"Key requests MCP access groups not allowed by team '{team_obj.team_id}': " + f"Key requests MCP access groups not allowed by team '{team_id}': " f"{sorted(disallowed_groups)}. " f"Team allows: {sorted(team_access_groups)}." ) @@ -390,58 +392,14 @@ async def validate_key_mcp_servers_against_team( if team_mcp_toolsets: disallowed_toolsets = requested_toolsets - set(team_mcp_toolsets) if disallowed_toolsets: + team_id = team_obj.team_id raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={ "error": ( - f"Key requests MCP toolsets not allowed by team '{team_obj.team_id}': " + f"Key requests MCP toolsets not allowed by team '{team_id}': " f"{sorted(disallowed_toolsets)}. " f"Team allows: {sorted(team_mcp_toolsets)}." ) }, ) - - -def _extract_requested_search_tools(object_permission: Optional[dict]) -> List[str]: - """Return search_tool_name values from a key's object_permission dict.""" - if not object_permission or not isinstance(object_permission, dict): - return [] - raw = object_permission.get("search_tools") - if not isinstance(raw, list): - return [] - return [str(x) for x in raw if x] - - -async def validate_key_search_tools_against_team( - object_permission: Optional[dict], - team_obj: Optional["LiteLLM_TeamTableCachedObj"], -) -> None: - """ - Validate key object_permission.search_tools is a subset of the team's allowlist. - - Empty team allowlist means no restriction at team layer (skip). - """ - requested = _extract_requested_search_tools(object_permission) - if not requested: - return - - team_tools: List[str] = [] - if team_obj is not None and team_obj.object_permission is not None: - st = team_obj.object_permission.search_tools - if st: - team_tools = list(st) - - if not team_tools: - return - - disallowed = set(requested) - set(team_tools) - if disallowed: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail={ - "error": ( - f"Key requests search tools not allowed by team '{team_obj.team_id}': " - f"{sorted(disallowed)}. Team allows: {sorted(team_tools)}." - ) - }, - ) From 28bcad34084919ab8a6fdfde5cbccad1a845091d Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 12:55:35 +0530 Subject: [PATCH 10/14] Fix mypy --- .../object_permission_utils.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/litellm/proxy/management_helpers/object_permission_utils.py b/litellm/proxy/management_helpers/object_permission_utils.py index 552478f7b5..1e5c575a23 100644 --- a/litellm/proxy/management_helpers/object_permission_utils.py +++ b/litellm/proxy/management_helpers/object_permission_utils.py @@ -403,3 +403,47 @@ async def validate_key_mcp_servers_against_team( ) }, ) + +def _extract_requested_search_tools(object_permission: Optional[dict]) -> List[str]: + """Return search_tool_name values from a key's object_permission dict.""" + if not object_permission or not isinstance(object_permission, dict): + return [] + raw = object_permission.get("search_tools") + if not isinstance(raw, list): + return [] + return [str(x) for x in raw if x] + + +async def validate_key_search_tools_against_team( + object_permission: Optional[dict], + team_obj: Optional["LiteLLM_TeamTableCachedObj"], +) -> None: + """ + Validate key object_permission.search_tools is a subset of the team's allowlist. + + Empty team allowlist means no restriction at team layer (skip). + """ + requested = _extract_requested_search_tools(object_permission) + if not requested: + return + + team_tools: List[str] = [] + if team_obj is not None and team_obj.object_permission is not None: + st = team_obj.object_permission.search_tools + if st: + team_tools = list(st) + + if not team_tools: + return + + disallowed = set(requested) - set(team_tools) + if disallowed: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail={ + "error": ( + f"Key requests search tools not allowed by team '{team_obj.team_id}': " + f"{sorted(disallowed)}. Team allows: {sorted(team_tools)}." + ) + }, + ) \ No newline at end of file From e34036045020863b170f1613d06fdf9c5c34dba3 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 13:00:49 +0530 Subject: [PATCH 11/14] Fix black --- litellm/proxy/management_helpers/object_permission_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/management_helpers/object_permission_utils.py b/litellm/proxy/management_helpers/object_permission_utils.py index 1e5c575a23..023009d198 100644 --- a/litellm/proxy/management_helpers/object_permission_utils.py +++ b/litellm/proxy/management_helpers/object_permission_utils.py @@ -404,6 +404,7 @@ async def validate_key_mcp_servers_against_team( }, ) + def _extract_requested_search_tools(object_permission: Optional[dict]) -> List[str]: """Return search_tool_name values from a key's object_permission dict.""" if not object_permission or not isinstance(object_permission, dict): @@ -446,4 +447,4 @@ async def validate_key_search_tools_against_team( f"{sorted(disallowed)}. Team allows: {sorted(team_tools)}." ) }, - ) \ No newline at end of file + ) From d38609eb99adb9fa18f5a3d2c968c9aa58d1fdf1 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 13:14:04 +0530 Subject: [PATCH 12/14] fix mypy --- litellm/proxy/management_helpers/object_permission_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/management_helpers/object_permission_utils.py b/litellm/proxy/management_helpers/object_permission_utils.py index 023009d198..eb90d1b5ca 100644 --- a/litellm/proxy/management_helpers/object_permission_utils.py +++ b/litellm/proxy/management_helpers/object_permission_utils.py @@ -439,11 +439,12 @@ async def validate_key_search_tools_against_team( disallowed = set(requested) - set(team_tools) if disallowed: + team_id = team_obj.team_id if team_obj is not None else "unknown" raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={ "error": ( - f"Key requests search tools not allowed by team '{team_obj.team_id}': " + f"Key requests search tools not allowed by team '{team_id}': " f"{sorted(disallowed)}. Team allows: {sorted(team_tools)}." ) }, From 45c22081ee45da0d477ca307cafd855ff69972e4 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 29 Apr 2026 17:47:27 +0530 Subject: [PATCH 13/14] Fix ui unit test --- .../src/components/view_logs/index.test.tsx | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/index.test.tsx b/ui/litellm-dashboard/src/components/view_logs/index.test.tsx index 427c55c92b..937844e1c1 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.test.tsx @@ -7,15 +7,25 @@ import type { Row } from "@tanstack/react-table"; import { renderWithProviders } from "../../../tests/test-utils"; const mockHandleFilterResetFromHook = vi.fn(); -vi.mock("./log_filter_logic", () => ({ - useLogFilterLogic: vi.fn(() => ({ - filters: {}, - filteredLogs: { data: [], total: 0, page: 1, page_size: 50, total_pages: 1 }, - allTeams: [], - handleFilterChange: vi.fn(), - handleFilterReset: mockHandleFilterResetFromHook, - })), -})); +vi.mock("./log_filter_logic", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + useLogFilterLogic: vi.fn(() => ({ + filters: {}, + filteredLogs: { + data: [], + total: 0, + page: 1, + page_size: 50, + total_pages: 1, + }, + allTeams: [], + handleFilterChange: vi.fn(), + handleFilterReset: mockHandleFilterResetFromHook, + })), + }; +}); vi.mock("../networking", async (importOriginal) => { const actual = await importOriginal(); From 4b9505bb9f2451e146a3abf04b58c1722b8f603c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 29 Apr 2026 21:08:19 +0000 Subject: [PATCH 14/14] fix: address Cursor Bugbot findings on PR #26691 - Remove unused search_provider parameter from SearchAPIRouter._resolve_search_provider_credentials. The function only reads tool_litellm_params; the docstring already omitted search_provider, confirming it was unintentional dead code. - Drop redundant hasAgents/hasSearchTools conditions from the outer object_permission guard in OldTeams.tsx. Both agent and search-tool handling already run independently below this block with their own object_permission initialization, so including them in the outer guard caused an empty object_permission to be created prematurely and never populated within that block. Co-authored-by: Mateo Wang --- litellm/router_utils/search_api_router.py | 2 -- ui/litellm-dashboard/src/components/OldTeams.tsx | 9 +-------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/litellm/router_utils/search_api_router.py b/litellm/router_utils/search_api_router.py index dc9cafceef..9bcbcd8365 100644 --- a/litellm/router_utils/search_api_router.py +++ b/litellm/router_utils/search_api_router.py @@ -23,7 +23,6 @@ class SearchAPIRouter: @staticmethod def _resolve_search_provider_credentials( *, - search_provider: str, tool_litellm_params: Dict[str, Any], ) -> Tuple[Optional[str], Optional[str]]: """ @@ -227,7 +226,6 @@ class SearchAPIRouter: ) api_key, api_base = SearchAPIRouter._resolve_search_provider_credentials( - search_provider=search_provider, tool_litellm_params=litellm_params, ) diff --git a/ui/litellm-dashboard/src/components/OldTeams.tsx b/ui/litellm-dashboard/src/components/OldTeams.tsx index 4edc1bd044..abc26c4cd4 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.tsx @@ -512,11 +512,6 @@ const Teams: React.FC = ({ } } - // Transform integrations into object_permission - const hasAgents = - formValues.allowed_agents_and_groups && - ((formValues.allowed_agents_and_groups.agents?.length ?? 0) > 0 || - (formValues.allowed_agents_and_groups.accessGroups?.length ?? 0) > 0); const hasSearchTools = Array.isArray(formValues.object_permission_search_tools) && formValues.object_permission_search_tools.length > 0; @@ -526,9 +521,7 @@ const Teams: React.FC = ({ (formValues.allowed_mcp_servers_and_groups && (formValues.allowed_mcp_servers_and_groups.servers?.length > 0 || formValues.allowed_mcp_servers_and_groups.accessGroups?.length > 0 || - formValues.allowed_mcp_servers_and_groups.toolPermissions)) || - hasAgents || - hasSearchTools + formValues.allowed_mcp_servers_and_groups.toolPermissions)) ) { if (!formValues.object_permission) { formValues.object_permission = {};