From 50c90281f4fda1b080c2c4be91d8065f14f6f973 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Thu, 2 Jul 2026 13:24:22 -0700 Subject: [PATCH 001/399] feat(mcp): add true_passthrough and oauth_delegate auth modes Introduce two first-class MCP server auth_type values that make LiteLLM's role in upstream authentication explicit, added alongside the existing delegate_auth_to_upstream / oauth_passthrough flags without changing their behavior. true_passthrough is a transparent proxy: LiteLLM performs no admission auth, requires no x-litellm-api-key, mints/stores/refreshes nothing, and forwards the client's Authorization to the upstream exactly as received. oauth_delegate keeps normal LiteLLM admission (x-litellm-api-key / SSO / JWT) and then forwards the client's separate upstream Authorization unchanged; the admission credential is never forwarded upstream. Both modes forward the caller's token via the existing extra_headers path and defer egress credential resolution to v1 (the v2 to_server_spec returns None for them). Upstream 401/403 responses are surfaced rather than swallowed so upstream OAuth challenges are preserved. Servers in either mode require per-user auth, so userless health checks are skipped. --- .../mcp_server/auth/user_api_key_auth_mcp.py | 33 + .../mcp_server/mcp_server_manager.py | 95 ++- .../outbound_credentials/adapter.py | 10 +- .../outbound_credentials/resolver.py | 23 +- .../proxy/_experimental/mcp_server/server.py | 8 + litellm/types/mcp.py | 4 + .../types/mcp_server/mcp_server_manager.py | 15 + .../auth/test_user_api_key_auth_mcp.py | 677 +++++++----------- .../outbound_credentials/test_adapter.py | 7 + .../outbound_credentials/test_resolver.py | 25 +- .../mcp_server/test_mcp_server_manager.py | 223 ++++++ ui/litellm-dashboard/src/lib/http/schema.d.ts | 2 +- 12 files changed, 676 insertions(+), 446 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index d2986a3cd82..9f28da19292 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -220,6 +220,12 @@ class MCPRequestHandler: # when EVERY target is auth_type=oauth2 with delegate_auth_to_upstream # set; fails closed otherwise. validated_user_api_key_auth = UserAPIKeyAuth() + elif MCPRequestHandler._target_servers_are_true_passthrough( + path=request_route, + mcp_servers=mcp_servers, + client_ip=IPAddressUtils.get_mcp_client_ip(request), + ): + validated_user_api_key_auth = UserAPIKeyAuth() elif oauth2_headers: # Authorization on a non-delegated server: the bearer must be a real # LiteLLM credential, so a failed validation is a genuine 401/403 and @@ -399,6 +405,33 @@ class MCPRequestHandler: return False return True + @staticmethod + def _target_servers_are_true_passthrough( + path: str, mcp_servers: Optional[list[str]], client_ip: Optional[str] + ) -> bool: + """ + True only when EVERY MCP server the request targets is ``auth_type == true_passthrough``. + Fails closed when any target does not opt in or cannot be resolved. + + Used by :meth:`process_mcp_request` to skip LiteLLM admission auth entirely: the gateway is a + transparent proxy and the caller's ``Authorization`` is an upstream token, never a LiteLLM key. + Mirrors :meth:`_target_servers_delegate_auth_to_upstream`; a mixed-target request keeps normal auth. + """ + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.types.mcp import MCPAuth + + target_names = MCPRequestHandler._resolve_target_server_names(path=path, mcp_servers_header=mcp_servers) + if not target_names: + return False + + for name in target_names: + server = global_mcp_server_manager.get_mcp_server_by_name(name, client_ip=client_ip) + if server is None or server.auth_type != MCPAuth.true_passthrough: + return False + return True + @staticmethod def _resolve_target_server_names(path: str, mcp_servers_header: Optional[List[str]]) -> List[str]: """ diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 7c88c903324..fd978e5bfc9 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -78,6 +78,7 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchange_ ) from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( AuthorizationCodeConfig, + PassthroughConfig, ServerSpec, TokenExchangeConfig, ) @@ -213,6 +214,13 @@ def _should_strip_caller_authorization( pass-through cold-start case (RFC 9728) the bearer in ``Authorization`` is the upstream OAuth token and must be forwarded, so we keep it. + - **oauth_delegate servers**: admission always runs and there is no + anonymous path, so the caller's separate ``Authorization`` is + forwarded only when a distinct ``x-litellm-api-key`` carried + admission. Without that header the ``Authorization`` *was* the + admission credential — a virtual key, an IdP JWT, or an SSO / OIDC / + session token whose ``api_key`` is ``None`` — and must never reach + the upstream, so it is stripped regardless of the ``api_key`` value. """ if mcp_server.auth_type == MCPAuth.oauth2_token_exchange: # OBO: the inbound Authorization is the subject token. It is exchanged at the IdP and only the @@ -226,11 +234,13 @@ def _should_strip_caller_authorization( # upstream — it would override another user's stored credential. Delegate and # pass-through return None from to_server_spec and keep forwarding the bearer. return True - if not mcp_server.is_oauth_passthrough: + if not (mcp_server.is_oauth_passthrough or mcp_server.is_oauth_delegate): return False normalized_raw_headers = {str(k).lower(): v for k, v in (raw_headers or {}).items() if isinstance(k, str)} has_explicit_litellm_admission_header = normalized_raw_headers.get("x-litellm-api-key") is not None + if mcp_server.is_oauth_delegate: + return not has_explicit_litellm_admission_header admission_consumed_authorization_as_litellm_key = ( user_api_key_auth is not None and bool(getattr(user_api_key_auth, "api_key", None)) @@ -323,6 +333,41 @@ async def _resolve_byok_mcp_auth_header( return mcp_auth_header +def _client_forwarded_authorization_headers( + mcp_server: MCPServer, + oauth2_headers: Optional[dict[str, str]], + raw_headers: Optional[dict[str, str]], + user_api_key_auth: Optional[UserAPIKeyAuth], +) -> Optional[dict[str, str]]: + """Egress headers for the client-forwarded-token modes (``true_passthrough`` / ``oauth_delegate``). + + Forwards the caller's ``Authorization`` to the upstream, stripped when + ``_should_strip_caller_authorization`` says it was consumed as the LiteLLM admission key. Shared by + ``_call_regular_mcp_tool`` and ``server.py``'s ``_prepare_mcp_server_headers`` so the two egress + paths cannot drift, mirroring the ``_should_strip_caller_authorization`` split. + """ + extra_headers = oauth2_headers.copy() if oauth2_headers else None + if extra_headers and _should_strip_caller_authorization( + mcp_server=mcp_server, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ): + return _without_authorization(extra_headers) + return extra_headers + + +def _take_forwarded_authorization( + headers: Optional[dict[str, str]], +) -> tuple[Optional[str], Optional[dict[str, str]]]: + """Pop the ``Authorization`` value out of ``headers`` (case-insensitive), returning it with the + remaining headers, so the passthrough resolver arm is the single Authorization source rather than + the header also riding in ``extra_headers`` (which the resolved auth would then defer to).""" + if not headers: + return None, headers + value = next((v for k, v in headers.items() if k.lower() == "authorization"), None) + return value, _without_authorization(headers) + + def _extract_upstream_auth_failure( exc: BaseException, ) -> Optional[tuple[int, Optional[str]]]: @@ -1623,15 +1668,18 @@ class MCPServerManager: delegate_server_ids = [ server.server_id for server in self.get_registry().values() - if getattr(server, "auth_type", None) == MCPAuth.oauth2 - and getattr(server, "delegate_auth_to_upstream", False) is True - # M2M servers must not be exposed anonymously: an - # unauthenticated caller would get LiteLLM to proxy tool - # calls using its stored client_credentials. Resolve the flow - # rather than reading has_client_credentials so an unstamped - # M2M-shape row (null column, verbatim-read as non-M2M) still - # fails closed here, matching the anonymous-delegate auth gate. - and MCPServerManager.effective_oauth2_flow(server) != "client_credentials" + if ( + getattr(server, "auth_type", None) == MCPAuth.oauth2 + and getattr(server, "delegate_auth_to_upstream", False) is True + # M2M servers must not be exposed anonymously: an + # unauthenticated caller would get LiteLLM to proxy tool + # calls using its stored client_credentials. Resolve the flow + # rather than reading has_client_credentials so an unstamped + # M2M-shape row (null column, verbatim-read as non-M2M) still + # fails closed here, matching the anonymous-delegate auth gate. + and MCPServerManager.effective_oauth2_flow(server) != "client_credentials" + ) + or getattr(server, "auth_type", None) == MCPAuth.true_passthrough ] combined_servers.update(delegate_server_ids) @@ -2229,16 +2277,17 @@ class MCPServerManager: spec = None if transport == MCPTransport.stdio else to_server_spec(server) provider = cred_provider or self._cred_provider # A caller-supplied per-request override (mcp_auth_header / x-mcp-*) defers to the v1 path - # so it wins - except for the per-user modes the v2 resolver owns (authorization_code's - # stored token and token_exchange's RFC 8693 minted token). A caller must not be able to - # substitute another user's stored credential, nor silently disable the OBO exchange and - # forward an arbitrary bearer upstream, so we keep the v2 spec and ignore the override for - # both; the REST tools preview supplies its not-yet-persisted token through the resolver - # (cred_provider), never this path. + # so it wins - except for the modes the v2 resolver owns per-caller (authorization_code's + # stored token, token_exchange's RFC 8693 minted token, and the passthrough modes' + # forwarded caller token). A caller must not be able to substitute another user's stored + # credential, nor silently disable the OBO exchange and forward an arbitrary bearer + # upstream, so we keep the v2 spec and ignore the override for these; the REST tools + # preview supplies its not-yet-persisted token through the resolver (cred_provider), + # never this path. if ( spec is not None and mcp_auth_header - and not isinstance(spec.config, (AuthorizationCodeConfig, TokenExchangeConfig)) + and not isinstance(spec.config, (AuthorizationCodeConfig, PassthroughConfig, TokenExchangeConfig)) ): spec = None auth_value = ( @@ -2305,11 +2354,14 @@ class MCPServerManager: server_url = server.url or "" if spec is not None: + inbound_token = subject_token + if isinstance(spec.config, PassthroughConfig): + inbound_token, extra_headers = _take_forwarded_authorization(extra_headers) resolved_auth, extra_headers = await self._resolve_v2_auth( server=server, spec=spec, provider=provider, - subject_token=subject_token, + subject_token=inbound_token, user_api_key_auth=user_api_key_auth, extra_headers=extra_headers, ) @@ -3730,6 +3782,13 @@ class MCPServerManager: user_api_key_auth=user_api_key_auth, ): extra_headers = _without_authorization(extra_headers) + elif mcp_server.is_true_passthrough or mcp_server.is_oauth_delegate: + extra_headers = _client_forwarded_authorization_headers( + mcp_server=mcp_server, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) if mcp_server.extra_headers and raw_headers: if extra_headers is None: diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py index 05896bfff74..e87e8081ced 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py @@ -23,6 +23,7 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( AuthorizationCodeConfig, CredError, NoneConfig, + PassthroughConfig, ServerSpec, SharedKey, Subject, @@ -62,9 +63,10 @@ def to_server_spec(server: MCPServer) -> Optional[ServerSpec]: an ``assert_never`` tail, so a newly added auth mode fails the type gate here until it is explicitly mapped or explicitly deferred, rather than silently falling through to v1. Live modes: ``none``, the static-header family (``api_key`` plus the Authorization schemes, - all shared-key), ``oauth2`` per-user tokens (``authorization_code``), and - ``oauth2_token_exchange`` (OBO); client_credentials (M2M), delegated/passthrough - oauth2, and SigV4 return None and stay on v1. + all shared-key), ``oauth2`` per-user tokens (``authorization_code``), ``oauth2_token_exchange`` + (OBO), and the client-forwarded token modes ``true_passthrough`` / ``oauth_delegate`` + (``PassthroughConfig``); client_credentials (M2M), delegated/passthrough oauth2, and SigV4 + return None and stay on v1. """ if server.is_byok: return None # per-user BYOK source not migrated yet -> defer to v1 (any auth_type) @@ -94,6 +96,8 @@ def to_server_spec(server: MCPServer) -> Optional[ServerSpec]: ) # client_credentials (M2M) and delegate/passthrough oauth2 stay on v1 return None + case MCPAuth.true_passthrough | MCPAuth.oauth_delegate: + return ServerSpec(server_id=server.server_id, resource=resource, config=PassthroughConfig()) case MCPAuth.oauth2_token_exchange: return _token_exchange_spec(server, resource) case MCPAuth.aws_sigv4: diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py index c82ce1037d6..ecfd471190c 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py @@ -7,10 +7,11 @@ no precedence cascade. It is wildcard-free with an `assert_never` tail, so addin an arm fails the type gate (basedpyright `reportMatchNotExhaustive`); a bypassed gate fails loudly at runtime instead of returning `None`. -`none` and `api_key` (shared-key source) are live, as is `authorization_code`, which reads the -user's token from the injected `OAuthTokenStore`, and `token_exchange`, which swaps the caller's -inbound token through the injected `TokenExchanger`. The remaining arms are `not_implemented` stubs -that each land in a follow-up PR with their seam. Pure v2: no imports from v1. +`none`, `api_key` (shared-key source), and `passthrough` (forwards the caller's own inbound token) +are live, as is `authorization_code`, which reads the user's token from the injected +`OAuthTokenStore`, and `token_exchange`, which swaps the caller's inbound token through the +injected `TokenExchanger`. The remaining arms are `not_implemented` stubs that each land in a +follow-up PR with their seam. Pure v2: no imports from v1. """ from __future__ import annotations @@ -97,7 +98,7 @@ class UpstreamCredentialProvider: case ApiKeyConfig() as config: return self._api_key(config) case PassthroughConfig(): - return _not_implemented(AuthSpecKind.passthrough) + return self._passthrough(subject) case ClientCredentialsConfig(): return _not_implemented(AuthSpecKind.client_credentials) case TokenExchangeConfig() as config: @@ -118,6 +119,18 @@ class UpstreamCredentialProvider: """ return await self._authz_token(subject, server) is not None + def _passthrough(self, subject: Subject) -> Result[httpx.Auth, CredError]: + """Forward the caller's own upstream credential verbatim; the gateway mints nothing. + + The inbound token is the caller's already-disambiguated ``Authorization`` (never the LiteLLM + admission credential; the edge adapter drops that before building the ``Subject``). When it is + absent the request is sent unauthenticated so the upstream's own 401 surfaces, rather than the + gateway challenging on the upstream's behalf. + """ + if subject.inbound_token is None: + return Ok(NoOpAuth()) + return Ok(StaticHeaderAuth(subject.inbound_token.get_secret_value(), header_name="Authorization")) + def _api_key(self, config: ApiKeyConfig) -> Result[httpx.Auth, CredError]: match config.key_source: case SharedKey() as source: diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index e3812522ded..96fd014fbe1 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -331,6 +331,7 @@ if MCP_AVAILABLE: ) from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( MCPServerManager, + _client_forwarded_authorization_headers, _should_strip_caller_authorization, _without_authorization, global_mcp_server_manager, @@ -1561,6 +1562,13 @@ if MCP_AVAILABLE: user_api_key_auth=user_api_key_auth, ): extra_headers = _without_authorization(extra_headers) + elif server.is_true_passthrough or server.is_oauth_delegate: + extra_headers = _client_forwarded_authorization_headers( + mcp_server=server, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) if server.extra_headers and raw_headers: if extra_headers is None: diff --git a/litellm/types/mcp.py b/litellm/types/mcp.py index 9c564a3c7a6..d273e8ec4db 100644 --- a/litellm/types/mcp.py +++ b/litellm/types/mcp.py @@ -38,6 +38,8 @@ class MCPAuth(str, enum.Enum): aws_sigv4 = "aws_sigv4" token = "token" oauth2_token_exchange = "oauth2_token_exchange" + true_passthrough = "true_passthrough" + oauth_delegate = "oauth_delegate" # RFC 8693 default subject_token_type. A NULL column / omitted config key means @@ -60,6 +62,8 @@ MCPAuthType = Optional[ MCPAuth.aws_sigv4, MCPAuth.token, MCPAuth.oauth2_token_exchange, + MCPAuth.true_passthrough, + MCPAuth.oauth_delegate, ] ] diff --git a/litellm/types/mcp_server/mcp_server_manager.py b/litellm/types/mcp_server/mcp_server_manager.py index 522a6f09165..f102ab5b7b9 100644 --- a/litellm/types/mcp_server/mcp_server_manager.py +++ b/litellm/types/mcp_server/mcp_server_manager.py @@ -152,6 +152,18 @@ class MCPServer(BaseModel): """True if this is an OAuth2 server that relies on per-user tokens (no client_credentials).""" return self.auth_type == MCPAuth.oauth2 and not self.has_client_credentials + @property + def is_true_passthrough(self) -> bool: + """True for the transparent-proxy mode: LiteLLM performs no admission auth and forwards the + client's ``Authorization`` to the upstream unchanged.""" + return self.auth_type == MCPAuth.true_passthrough + + @property + def is_oauth_delegate(self) -> bool: + """True for the delegated-upstream-OAuth mode: LiteLLM still admits the caller (API key / SSO / + JWT) but forwards the caller's separate upstream ``Authorization`` unchanged, minting nothing.""" + return self.auth_type == MCPAuth.oauth_delegate + @property def requires_per_user_auth(self) -> bool: """ @@ -167,6 +179,9 @@ class MCPServer(BaseModel): if self.needs_user_oauth_token: return True + if self.is_true_passthrough or self.is_oauth_delegate: + return True + # PAT passthrough: auth_type is none but extra_headers includes auth headers if self.auth_type == MCPAuth.none and self.extra_headers: auth_header_names = {"authorization", "x-api-key", "api-key", "apikey"} diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index 3db0f8540f9..c984ccb783e 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -1,14 +1,12 @@ import json import os import sys -from unittest.mock import AsyncMock, MagicMock, call as mock_call, patch +from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi.testclient import TestClient -sys.path.insert( - 0, os.path.abspath("../../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path from starlette.datastructures import Headers @@ -78,20 +76,14 @@ class TestMCPRequestHandler: ) # Mock the helper methods instead of database calls - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key_servers: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team_servers: # Set up return values mock_key_servers.return_value = key_servers mock_team_servers.return_value = team_servers # Call the method - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=mock_user_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=mock_user_auth) # Assert the result (order-independent comparison) assert sorted(result) == sorted(expected_result) @@ -148,20 +140,14 @@ class TestMCPRequestHandler: ) # Mock the helper functions - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key_servers: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team_servers: # Configure mocks to return the test data mock_key_servers.return_value = key_servers mock_team_servers.return_value = team_servers # Call the method - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth) # Assert the result (order-independent comparison) assert sorted(result) == sorted(expected_servers) @@ -186,9 +172,7 @@ class TestMCPRequestHandler: ): """The require_key_mcp_access_defined general setting flips an empty key from inheriting its team's MCP servers (default) to inheriting none.""" - auth = UserAPIKeyAuth( - api_key="test-key", user_id="test-user", team_id="test-team" - ) + auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user", team_id="test-team") with ( patch.object( MCPRequestHandler, @@ -272,23 +256,22 @@ class TestMCPRequestHandler: async def test_no_mcp_servers_sentinel_returns_empty(self, team_servers): """A key scoped to the no-mcp-servers sentinel resolves to zero servers, overriding team inheritance and never leaking the sentinel marker.""" - user_api_key_auth = UserAPIKeyAuth( - api_key="test-key", user_id="test-user", team_id="test-team" - ) + user_api_key_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user", team_id="test-team") key_object_permission = MagicMock() - key_object_permission.mcp_servers = [ - SpecialMCPServerNames.no_mcp_servers.value - ] + key_object_permission.mcp_servers = [SpecialMCPServerNames.no_mcp_servers.value] - with patch.object( - MCPRequestHandler, - "_get_key_object_permission", - return_value=key_object_permission, - ), patch.object( - MCPRequestHandler, - "_get_allowed_mcp_servers_for_team", - new_callable=AsyncMock, - return_value=team_servers, + with ( + patch.object( + MCPRequestHandler, + "_get_key_object_permission", + return_value=key_object_permission, + ), + patch.object( + MCPRequestHandler, + "_get_allowed_mcp_servers_for_team", + new_callable=AsyncMock, + return_value=team_servers, + ), ): result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth) @@ -309,9 +292,7 @@ class TestMCPRequestHandler: "_get_key_object_permission", return_value=key_object_permission, ): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert result == [SpecialMCPServerNames.no_mcp_servers.value] @@ -320,9 +301,7 @@ class TestMCPRequestHandler: # Test case: None values in database mock_prisma_client = MagicMock() - mock_prisma_client.db.litellm_objectpermissiontable.find_unique.return_value = ( - None - ) + mock_prisma_client.db.litellm_objectpermissiontable.find_unique.return_value = None mock_prisma_client.db.litellm_teamtable.find_unique.return_value = None user_api_key_auth = UserAPIKeyAuth( @@ -337,9 +316,7 @@ class TestMCPRequestHandler: assert result == [] # Test case: Exception handling - mock_prisma_client.db.litellm_objectpermissiontable.find_unique.side_effect = ( - Exception("DB Error") - ) + mock_prisma_client.db.litellm_objectpermissiontable.find_unique.side_effect = Exception("DB Error") with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth) @@ -384,15 +361,9 @@ class TestMCPRequestHandler: access_group_ids=["grp-mcp"], ) with ( - patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key, - patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team, - patch.object( - MCPRequestHandler, "_get_key_access_group_mcp_server_extras" - ) as mock_grants, + patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key, + patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team, + patch.object(MCPRequestHandler, "_get_key_access_group_mcp_server_extras") as mock_grants, ): mock_key.return_value = key_servers mock_team.return_value = team_servers @@ -414,13 +385,9 @@ class TestMCPRequestHandler: "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", new=AsyncMock(return_value=[]), ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - auth - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(auth) assert result == [] # expand_permission_list must not be reached when there are no raw ids. mock_mgr.expand_permission_list.assert_not_called() @@ -433,14 +400,10 @@ class TestMCPRequestHandler: "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", new=AsyncMock(return_value=["alias-a", "srv-b"]), ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.expand_permission_list.return_value = ["srv-a", "srv-b"] - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - auth - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(auth) assert sorted(result) == ["srv-a", "srv-b"] mock_mgr.expand_permission_list.assert_called_once_with(["alias-a", "srv-b"]) @@ -451,9 +414,7 @@ class TestMCPRequestHandler: "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", new=AsyncMock(side_effect=Exception("db down")), ): - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - auth - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(auth) assert result == [] @pytest.mark.parametrize( @@ -743,9 +704,7 @@ class TestMCPRequestHandler: # Verify MCP servers mcp_servers_header = extracted_headers.get(SpecialHeaders.mcp_servers.value) mcp_servers = None - if ( - mcp_servers_header is not None - ): # Changed from 'if mcp_servers_header:' to handle empty strings + if mcp_servers_header is not None: # Changed from 'if mcp_servers_header:' to handle empty strings try: # First try to parse as JSON array for backward compatibility try: @@ -754,16 +713,12 @@ class TestMCPRequestHandler: mcp_servers = None except (json.JSONDecodeError, TypeError, ValueError): # If JSON parsing fails, treat as comma-separated list - mcp_servers = [ - s.strip() for s in mcp_servers_header.split(",") if s.strip() - ] + mcp_servers = [s.strip() for s in mcp_servers_header.split(",") if s.strip()] except Exception: mcp_servers = None # If we got an empty string or parsing resulted in no servers, return empty list - if mcp_servers_header == "" or ( - mcp_servers is not None and len(mcp_servers) == 0 - ): + if mcp_servers_header == "" or (mcp_servers is not None and len(mcp_servers) == 0): mcp_servers = [] assert mcp_servers == expected_result["mcp_servers"] @@ -833,9 +788,7 @@ class TestMCPOAuth2AuthFlow: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", new_callable=AsyncMock, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = oauth2_server ( @@ -851,10 +804,7 @@ class TestMCPOAuth2AuthFlow: # The upstream token is never validated as a LiteLLM key ... mock_auth.assert_not_called() # ... and is preserved for upstream forwarding. - assert ( - oauth2_headers.get("Authorization") - == "Bearer atlassian-oauth2-access-token-xyz" - ) + assert oauth2_headers.get("Authorization") == "Bearer atlassian-oauth2-access-token-xyz" async def test_explicit_litellm_key_with_oauth2_authorization(self): """ @@ -893,9 +843,7 @@ class TestMCPOAuth2AuthFlow: assert call_args.kwargs["api_key"] == "sk-litellm-valid-key" # OAuth2 headers should still contain the Authorization token - assert ( - oauth2_headers.get("Authorization") == "Bearer atlassian-oauth2-token" - ) + assert oauth2_headers.get("Authorization") == "Bearer atlassian-oauth2-token" async def test_litellm_key_in_authorization_backward_compat(self): """ @@ -997,9 +945,7 @@ class TestMCPOAuth2AuthFlow: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_proxy_exception, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = oauth2_server with pytest.raises(ProxyException) as exc_info: @@ -1068,9 +1014,7 @@ class TestMCPPublicRouteGuard: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): # Explicit unresolvable target — proves auth still fails even # when the registry has no info to fall back to. @@ -1101,9 +1045,7 @@ class TestMCPPublicRouteGuard: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -1157,9 +1099,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp._is_mcp_passthrough_cold_start" ) as mock_cold_start, @@ -1199,9 +1139,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1229,9 +1167,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1263,18 +1199,14 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.IPAddressUtils.get_mcp_client_ip", return_value="203.0.113.10", ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) assert exc_info.value.status_code == 401 - mock_mgr.get_mcp_server_by_name.assert_any_call( - "passthrough_server", client_ip="203.0.113.10" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("passthrough_server", client_ip="203.0.113.10") async def test_cold_start_propagates_non_401_http_error(self): from fastapi import HTTPException @@ -1294,9 +1226,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_forbidden, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1329,9 +1259,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_server_error, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1357,9 +1285,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1367,9 +1293,7 @@ class TestMCPPassthroughColdStartAdmission: auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) - mock_mgr.get_mcp_server_by_name.assert_any_call( - "passthrough_server", client_ip="" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("passthrough_server", client_ip="") async def test_cold_start_allows_proxy_exception_401_for_path_target(self): from litellm.proxy._types import ProxyException @@ -1394,9 +1318,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1404,9 +1326,7 @@ class TestMCPPassthroughColdStartAdmission: auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) - mock_mgr.get_mcp_server_by_name.assert_any_call( - "passthrough_server", client_ip="" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("passthrough_server", client_ip="") @pytest.mark.asyncio @@ -1450,12 +1370,10 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPOAuth2FallbackTargetGating._make_server(MCPAuth.api_key) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPOAuth2FallbackTargetGating._make_server( + MCPAuth.api_key ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1483,9 +1401,7 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -1523,12 +1439,10 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPOAuth2FallbackTargetGating._make_server(MCPAuth.oauth2) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPOAuth2FallbackTargetGating._make_server( + MCPAuth.oauth2 ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1562,15 +1476,11 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPOAuth2FallbackTargetGating._make_server( - auth_type=MCPAuth.none, - is_oauth_passthrough=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPOAuth2FallbackTargetGating._make_server( + auth_type=MCPAuth.none, + is_oauth_passthrough=True, ) auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) @@ -1598,9 +1508,7 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.IPAddressUtils.get_mcp_client_ip", return_value="203.0.113.10", ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -1612,9 +1520,7 @@ class TestMCPOAuth2FallbackTargetGating: # resolve to ``None`` (hidden by client IP) so neither bypass # opens. Use ``assert_any_call`` to assert the IP-scoped lookup # happened without locking the count. - mock_mgr.get_mcp_server_by_name.assert_any_call( - "hidden_oauth2_server", client_ip="203.0.113.10" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("hidden_oauth2_server", client_ip="203.0.113.10") async def test_fallback_blocked_when_any_target_in_header_is_not_oauth2(self): """ @@ -1649,9 +1555,7 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup with pytest.raises(HTTPException) as exc_info: @@ -1732,17 +1636,10 @@ class TestMCPDelegateAuthToUpstream: delegate_auth_to_upstream=True, available_on_public_internet=True, ) - assert ( - manager._build_mcp_server_table(delegated).delegate_auth_to_upstream is True - ) + assert manager._build_mcp_server_table(delegated).delegate_auth_to_upstream is True - not_delegated = delegated.model_copy( - update={"delegate_auth_to_upstream": False} - ) - assert ( - manager._build_mcp_server_table(not_delegated).delegate_auth_to_upstream - is False - ) + not_delegated = delegated.model_copy(update={"delegate_auth_to_upstream": False}) + assert manager._build_mcp_server_table(not_delegated).delegate_auth_to_upstream is False def test_build_mcp_server_table_preserves_oauth_passthrough(self): """Registry → API list rows must expose oauth_passthrough for the UI. @@ -1773,9 +1670,7 @@ class TestMCPDelegateAuthToUpstream: assert row.delegate_auth_to_upstream is False not_passthrough = passthrough.model_copy(update={"oauth_passthrough": False}) - assert ( - manager._build_mcp_server_table(not_passthrough).oauth_passthrough is False - ) + assert manager._build_mcp_server_table(not_passthrough).oauth_passthrough is False async def test_delegate_skips_litellm_auth_with_no_authorization(self): """ @@ -1796,15 +1691,11 @@ class TestMCPDelegateAuthToUpstream: patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) @@ -1834,15 +1725,11 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", new_callable=AsyncMock, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) ( auth_result, @@ -1880,15 +1767,11 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=False, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=False, ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1919,15 +1802,11 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.api_key, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.api_key, + delegate_auth_to_upstream=True, ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1971,9 +1850,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup with pytest.raises(HTTPException) as exc_info: @@ -2003,9 +1880,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -2034,15 +1909,11 @@ class TestMCPDelegateAuthToUpstream: new_callable=AsyncMock, return_value=UserAPIKeyAuth(user_id="real-user"), ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) @@ -2074,15 +1945,11 @@ class TestMCPDelegateAuthToUpstream: new_callable=AsyncMock, return_value=UserAPIKeyAuth(user_id="real-user"), ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) ( auth_result, @@ -2135,9 +2002,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = m2m_server # No delegate bypass → normal auth is attempted → 401 raised @@ -2188,9 +2053,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = legacy_m2m_server with pytest.raises(HTTPException) as exc_info: @@ -2235,9 +2098,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = pkce_server auth, *_rest = await MCPRequestHandler.process_mcp_request(scope) @@ -2278,9 +2139,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = internal_server auth, *_rest = await MCPRequestHandler.process_mcp_request(scope) @@ -2426,6 +2285,104 @@ class TestMCPDelegateAuthToUpstream: assert "public-server" in result assert "internal-server" in result + async def test_true_passthrough_skips_litellm_auth_anonymously(self): + """auth_type=true_passthrough performs no admission auth: the caller's Authorization is an + upstream token forwarded unchanged and user_api_key_auth is never called.""" + from litellm.types.mcp import MCPAuth + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/true_passthrough_server", + "headers": [(b"authorization", b"Bearer upstream-token")], + } + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + ): + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.true_passthrough, + ) + ( + auth_result, + _, + _, + _, + oauth2_headers, + _, + ) = await MCPRequestHandler.process_mcp_request(scope) + assert isinstance(auth_result, UserAPIKeyAuth) + assert auth_result.api_key is None + assert oauth2_headers.get("Authorization") == "Bearer upstream-token" + mock_auth.assert_not_called() + + async def test_true_passthrough_mixed_targets_fail_closed(self): + """One true_passthrough target mixed with a non-passthrough target must NOT skip admission.""" + from fastapi import HTTPException + + from litellm.types.mcp import MCPAuth + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp", + "headers": [(b"x-mcp-servers", b"tp_server,plain_server")], + } + + async def mock_user_api_key_auth_fails(api_key, request): + raise HTTPException(status_code=401, detail="Invalid API key") + + def mock_lookup(name, client_ip=None): + if name == "tp_server": + return TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.true_passthrough, + ) + return TestMCPDelegateAuthToUpstream._make_server(auth_type=MCPAuth.api_key) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_user_api_key_auth_fails, + ), + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + ): + mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + assert exc_info.value.status_code == 401 + + async def test_get_allowed_servers_includes_true_passthrough(self): + """Anonymous callers can reach true_passthrough servers; admission is delegated upstream.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + MCPServerManager, + ) + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + manager = MCPServerManager() + tp_server = MCPServer( + server_id="tp-server", + name="tp_server", + transport="http", + auth_type=MCPAuth.true_passthrough, + available_on_public_internet=True, + ) + manager.registry = {tp_server.server_id: tp_server} + + with patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[], + ): + result = await manager.get_allowed_mcp_servers(None) + + assert "tp-server" in result + def test_extract_target_server_names_matches_routing_parser(self): """ Regression: _extract_target_server_names_from_path must match the @@ -2464,13 +2421,12 @@ class TestMCPDelegateAuthToUpstream: ("/", []), ] for path_input, expected in cases: - assert ( - MCPRequestHandler._extract_target_server_names_from_path(path_input) - == expected - ), f"path={path_input!r} → expected {expected!r}" - assert ( - _get_mcp_servers_in_path(path_input) or [] - ) == expected, f"path={path_input!r} → routing expected {expected!r}" + assert MCPRequestHandler._extract_target_server_names_from_path(path_input) == expected, ( + f"path={path_input!r} → expected {expected!r}" + ) + assert (_get_mcp_servers_in_path(path_input) or []) == expected, ( + f"path={path_input!r} → routing expected {expected!r}" + ) async def test_delegate_does_not_bypass_on_extra_path_segment(self): """ @@ -2514,9 +2470,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = lookup_by_name with pytest.raises(HTTPException) as exc_info: @@ -2579,9 +2533,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = lookup_by_name # Bypass MUST NOT fire — path-derived target is the non-delegate @@ -2601,15 +2553,12 @@ class TestMCPDelegateAuthToUpstream: empty-list case, which fails closed). """ # Path matches /mcp/... — header is ignored. - assert MCPRequestHandler._resolve_target_server_names( - path="/mcp/foo", mcp_servers_header=["evil"] - ) == ["foo"] - assert MCPRequestHandler._resolve_target_server_names( - path="/mcp/foo,bar", mcp_servers_header=["evil"] - ) == ["foo", "bar"] - assert MCPRequestHandler._resolve_target_server_names( - path="/foo/mcp", mcp_servers_header=["evil"] - ) == ["foo"] + assert MCPRequestHandler._resolve_target_server_names(path="/mcp/foo", mcp_servers_header=["evil"]) == ["foo"] + assert MCPRequestHandler._resolve_target_server_names(path="/mcp/foo,bar", mcp_servers_header=["evil"]) == [ + "foo", + "bar", + ] + assert MCPRequestHandler._resolve_target_server_names(path="/foo/mcp", mcp_servers_header=["evil"]) == ["foo"] # Path does not match — header is trusted. assert MCPRequestHandler._resolve_target_server_names( path="/.well-known/oauth-authorization-server", @@ -2653,16 +2602,12 @@ class TestMCPCustomHeaderName: (None, "", "x-mcp-auth"), ], ) - def test_get_mcp_client_side_auth_header_name( - self, env_var, general_setting, expected_header_name - ): + def test_get_mcp_client_side_auth_header_name(self, env_var, general_setting, expected_header_name): """Test that custom header name configuration works correctly""" # Mock the secret manager and general settings with patch("litellm.secret_managers.main.get_secret_str") as mock_get_secret: - with patch( - "litellm.proxy.proxy_server.general_settings" - ) as mock_general_settings: + with patch("litellm.proxy.proxy_server.general_settings") as mock_general_settings: # Configure mocks mock_get_secret.return_value = env_var mock_general_settings.get.return_value = general_setting @@ -2685,9 +2630,7 @@ class TestMCPCustomHeaderName: if env_var is None: # When env var is None, general settings should be checked (twice if not None) expected_general_calls = 2 if general_setting is not None else 1 - assert ( - mock_general_settings.get.call_count == expected_general_calls - ) + assert mock_general_settings.get.call_count == expected_general_calls for call in mock_general_settings.get.call_args_list: assert call.args == ("mcp_client_side_auth_header_name",) else: @@ -2728,9 +2671,7 @@ class TestMCPCustomHeaderName: ), ], ) - def test_get_mcp_auth_header_from_headers_with_custom_name( - self, custom_header_name, headers, expected_auth_header - ): + def test_get_mcp_auth_header_from_headers_with_custom_name(self, custom_header_name, headers, expected_auth_header): """Test that MCP auth header extraction uses custom header name""" # Mock the header name method @@ -2749,9 +2690,7 @@ class TestMCPCustomHeaderName: extracted_headers = MCPRequestHandler._safe_get_headers_from_scope(scope) # Call the method - result = MCPRequestHandler._get_mcp_auth_header_from_headers( - extracted_headers - ) + result = MCPRequestHandler._get_mcp_auth_header_from_headers(extracted_headers) # Assert the result assert result == expected_auth_header @@ -2818,9 +2757,7 @@ class TestMCPCustomHeaderName: from starlette.datastructures import Headers # Test case 1: No server-specific headers - headers = Headers( - {"x-litellm-api-key": "test-key", "content-type": "application/json"} - ) + headers = Headers({"x-litellm-api-key": "test-key", "content-type": "application/json"}) result = MCPRequestHandler._get_mcp_server_auth_headers_from_headers(headers) assert result == {} @@ -2904,17 +2841,13 @@ class TestMCPCustomHeaderName: assert result == {"github_mcp": {"Authorization": "Bearer github-mcp-token"}} # Test case 8: Edge case - empty header value - headers = Headers( - {"x-litellm-api-key": "test-key", "x-mcp-github-authorization": ""} - ) + headers = Headers({"x-litellm-api-key": "test-key", "x-mcp-github-authorization": ""}) result = MCPRequestHandler._get_mcp_server_auth_headers_from_headers(headers) assert result == {"github": {"Authorization": ""}} # Test case 9: Edge case - very long header value long_token = "Bearer " + "x" * 1000 - headers = Headers( - {"x-litellm-api-key": "test-key", "x-mcp-github-authorization": long_token} - ) + headers = Headers({"x-litellm-api-key": "test-key", "x-mcp-github-authorization": long_token}) result = MCPRequestHandler._get_mcp_server_auth_headers_from_headers(headers) assert result == {"github": {"Authorization": long_token}} @@ -2980,9 +2913,7 @@ class TestMCPAccessGroupsE2E: # Assert the results assert auth_result.api_key == "test-api-key" assert mcp_auth_header is None - assert ( - mcp_servers is None - ) # x-mcp-access-groups is not parsed as mcp_servers + assert mcp_servers is None # x-mcp-access-groups is not parsed as mcp_servers assert mcp_server_auth_headers == {} # Verify the mock was called @@ -3097,9 +3028,7 @@ def test_mcp_path_based_server_segregation(monkeypatch): # Use TestClient to make a request to /mcp/zapier,group1/tools client = TestClient(app) - response = client.get( - "/mcp/zapier,group1/tools", headers={"x-litellm-api-key": "test"} - ) + response = client.get("/mcp/zapier,group1/tools", headers={"x-litellm-api-key": "test"}) assert response.status_code == 200 assert response.json() == {"status": "ok"} @@ -3177,15 +3106,11 @@ async def test_get_team_object_permission_with_already_loaded_permission(): mock_prisma, ): with patch("litellm.proxy.auth.auth_checks.get_team_object") as mock_get_team: - with patch( - "litellm.proxy.auth.auth_checks.get_object_permission" - ) as mock_get_perm: + with patch("litellm.proxy.auth.auth_checks.get_object_permission") as mock_get_perm: mock_get_team.return_value = mock_team_obj # Call the method - result = await MCPRequestHandler._get_team_object_permission( - mock_user_auth - ) + result = await MCPRequestHandler._get_team_object_permission(mock_user_auth) # Assert we got the object permission assert result == mock_object_permission @@ -3272,9 +3197,7 @@ async def test_get_team_object_permission_ui_session_team_skips_db_lookup(): mock_prisma = MagicMock() with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma): with patch("litellm.proxy.auth.auth_checks.get_team_object") as mock_get_team: - result = await MCPRequestHandler._get_team_object_permission( - mock_user_auth - ) + result = await MCPRequestHandler._get_team_object_permission(mock_user_auth) assert result is None mock_get_team.assert_not_called() @@ -3342,9 +3265,7 @@ async def test_get_allowed_tools_for_server_ui_session_team_keeps_key_restrictio detail={"error": "Team doesn't exist in db. Team=litellm-dashboard."}, ), ): - with patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ): + with patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm): result = await MCPRequestHandler.get_allowed_tools_for_server( server_id="server_1", user_api_key_auth=user_api_key_auth, @@ -3410,9 +3331,7 @@ async def test_get_allowed_mcp_servers_for_team_uses_helper(): return_value=["group-server1", "group-server2"], ) as mock_get_access_group_servers, ): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_team( - mock_user_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_team(mock_user_auth) assert set(result) == { "direct-server1", @@ -3455,9 +3374,7 @@ async def test_get_allowed_mcp_servers_for_team_with_no_object_permission(): return_value=mock_team, ), ): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_team( - mock_user_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_team(mock_user_auth) assert result == [] @@ -3507,9 +3424,7 @@ async def test_get_allowed_mcp_servers_for_team_without_team_id_returns_empty(): ), ], ) -async def test_get_allowed_mcp_servers_for_key_guard_conditions( - user_api_key_auth, prisma_client_value, scenario -): +async def test_get_allowed_mcp_servers_for_key_guard_conditions(user_api_key_auth, prisma_client_value, scenario): """Ensure guard clauses return [] before hitting get_object_permission.""" with patch( @@ -3517,9 +3432,7 @@ async def test_get_allowed_mcp_servers_for_key_guard_conditions( new_callable=AsyncMock, ) as mock_get_perm: with patch("litellm.proxy.proxy_server.prisma_client", prisma_client_value): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert result == [] mock_get_perm.assert_not_called() @@ -3546,9 +3459,7 @@ async def test_get_allowed_mcp_servers_for_key_returns_empty_when_db_returns_non ): mock_get_perm.return_value = None - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert result == [] mock_get_perm.assert_awaited_once() @@ -3591,14 +3502,10 @@ async def test_get_allowed_mcp_servers_for_key_prefers_in_memory_permission(): "litellm.proxy.auth.auth_checks.get_object_permission", new_callable=AsyncMock, ) as mock_get_perm: - with patch.object( - MCPRequestHandler, "_get_mcp_servers_from_access_groups" - ) as mock_access_groups: + with patch.object(MCPRequestHandler, "_get_mcp_servers_from_access_groups") as mock_access_groups: mock_access_groups.return_value = ["group-server"] - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert set(result) == {"direct-server", "group-server"} mock_get_perm.assert_not_called() @@ -3619,21 +3526,13 @@ class TestAgentMCPPermissions: team_id="test-team", agent_id="agent-123", ) - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_agent" - ) as mock_agent: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_agent") as mock_agent: mock_key.return_value = ["server_1", "server_2"] mock_team.return_value = [] mock_agent.return_value = ["server_1"] - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=user_api_key_auth) assert sorted(result) == ["server_1"] mock_agent.assert_called_once_with(user_api_key_auth) @@ -3644,21 +3543,13 @@ class TestAgentMCPPermissions: user_id="test-user", agent_id="agent-456", ) - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_agent" - ) as mock_agent: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_agent") as mock_agent: mock_key.return_value = ["server_1", "server_2"] mock_team.return_value = [] mock_agent.return_value = [] # no agent-level restriction - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=user_api_key_auth) assert sorted(result) == ["server_1", "server_2"] mock_agent.assert_called_once_with(user_api_key_auth) @@ -3669,21 +3560,13 @@ class TestAgentMCPPermissions: user_id="test-user", agent_id="agent-789", ) - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_agent" - ) as mock_agent: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_agent") as mock_agent: mock_key.return_value = ["server_1", "server_2"] mock_team.return_value = [] mock_agent.return_value = ["server_2", "server_3"] - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=user_api_key_auth) assert sorted(result) == ["server_2"] async def test_get_allowed_tools_for_server_agent_intersection(self): @@ -3696,9 +3579,7 @@ class TestAgentMCPPermissions: key_perm = MagicMock() key_perm.mcp_tool_permissions = {"server_1": ["tool_a", "tool_b"]} team_perm = None - with patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ): + with patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm): with patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -3730,9 +3611,7 @@ class TestAgentMCPPermissions: ) key_perm = MagicMock() key_perm.mcp_tool_permissions = {"server_1": ["tool_a", "tool_b"]} - with patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ): + with patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm): with patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -3762,9 +3641,7 @@ class TestAgentMCPPermissions: agent_row = MagicMock() agent_row.object_permission_id = "perm-xyz" prisma_client = MagicMock() - prisma_client.db.litellm_agentstable.find_unique = AsyncMock( - return_value=agent_row - ) + prisma_client.db.litellm_agentstable.find_unique = AsyncMock(return_value=agent_row) user_api_key_auth = UserAPIKeyAuth( api_key="test-key", user_id="test-user", @@ -3782,9 +3659,7 @@ class TestAgentMCPPermissions: return_value=expected_perm, ) as mock_get_perm, ): - result = await MCPRequestHandler._get_agent_object_permission( - user_api_key_auth - ) + result = await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) assert result is expected_perm mock_get_perm.assert_awaited_once() assert mock_get_perm.await_args.kwargs["object_permission_id"] == "perm-xyz" @@ -3804,9 +3679,7 @@ class TestAgentMCPPermissions: agent_row = MagicMock() agent_row.object_permission_id = None prisma_client = MagicMock() - prisma_client.db.litellm_agentstable.find_unique = AsyncMock( - return_value=agent_row - ) + prisma_client.db.litellm_agentstable.find_unique = AsyncMock(return_value=agent_row) user_api_key_auth = UserAPIKeyAuth( api_key="test-key", user_id="test-user", @@ -3822,14 +3695,8 @@ class TestAgentMCPPermissions: new_callable=AsyncMock, ) as mock_get_perm, ): - assert ( - await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) - is None - ) - assert ( - await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) - is None - ) + assert await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) is None + assert await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) is None mock_get_perm.assert_not_awaited() prisma_client.db.litellm_agentstable.find_unique.assert_awaited_once() @@ -3870,9 +3737,7 @@ async def test_tool_permission_servers_included_in_allowed_servers(): ) with ( - patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=perm - ), + patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=perm), patch.object( MCPRequestHandler, "_get_mcp_servers_from_access_groups", @@ -4105,9 +3970,7 @@ class TestOrgMCPPermissions: org_perm.mcp_tool_permissions = {"server_1": ["tool_a", "tool_b"]} with ( - patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ), + patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm), patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -4137,9 +4000,7 @@ class TestOrgMCPPermissions: org_perm.mcp_tool_permissions = {} with ( - patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ), + patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm), patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -4231,9 +4092,7 @@ async def test_mcp_key_access_group_extras_when_team_authorized(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == ["srv-stripe"] finally: _stop_patches(patches) @@ -4270,9 +4129,7 @@ async def test_mcp_key_access_group_extras_when_key_directly_authorized(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == ["srv-stripe"] finally: _stop_patches(patches) @@ -4286,9 +4143,7 @@ async def test_mcp_key_access_group_extras_when_key_has_no_groups(): access_group_ids=[], team_id="team-a", ) - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == [] @@ -4315,9 +4170,7 @@ async def test_mcp_key_access_group_extras_when_group_has_no_servers(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == [] finally: _stop_patches(patches) @@ -4351,9 +4204,7 @@ async def test_mcp_key_access_group_extras_granted_even_when_group_authorizes_ne ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == ["srv-finance-only"] finally: _stop_patches(patches) @@ -4376,9 +4227,7 @@ async def test_mcp_key_access_group_extras_when_get_access_object_raises(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == [] finally: _stop_patches(patches) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py index fbe768e07c6..17960e917a4 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py @@ -23,6 +23,7 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( AuthorizationCodeConfig, CredError, NoneConfig, + PassthroughConfig, SharedKey, TokenExchangeConfig, ) @@ -234,6 +235,12 @@ def test_token_exchange_empty_subject_token_type_normalizes_to_default(): assert spec.config.subject_token_type == "urn:ietf:params:oauth:token-type:access_token" +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +def test_client_forwarded_modes_map_to_passthrough_config(auth_type): + spec = to_server_spec(_server(auth_type=auth_type)) + assert spec is not None and isinstance(spec.config, PassthroughConfig) + + @pytest.mark.parametrize( "server", [ diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py index f8e45b38b49..64226eea821 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py @@ -1,9 +1,9 @@ """Tests for the resolver dispatch: live arms produce auth, stubbed arms fail closed. -`none`, `api_key` (shared-key source), `authorization_code`, and `token_exchange` are implemented; -every other arm, plus the `api_key` BYOK source, returns a typed `not_implemented` error until its -mode lands. Parametrizing the stubs over one config each also guards reachability: a dropped `case` -would hit `assert_never` and raise instead of returning the stub. +`none`, `api_key` (shared-key source), `passthrough`, `authorization_code`, and `token_exchange` are +implemented; every other arm, plus the `api_key` BYOK source, returns a typed `not_implemented` error +until its mode lands. Parametrizing the stubs over one config each also guards reachability: a dropped +`case` would hit `assert_never` and raise instead of returning the stub. """ import httpx @@ -253,9 +253,24 @@ async def test_token_exchange_without_an_exchanger_fails_closed(): assert result.error.tag == "misconfigured" +@pytest.mark.asyncio +async def test_passthrough_forwards_the_inbound_token_verbatim(): + subject = Subject(tenant_id="", subject_id="", inbound_token=SecretStr("Bearer upstream-xyz")) + result = await UpstreamCredentialProvider().resolve_credentials(subject, _spec(PassthroughConfig())) + assert isinstance(result, Ok) + assert isinstance(result.ok, StaticHeaderAuth) + assert _emitted(result.ok)["Authorization"] == "Bearer upstream-xyz" + + +@pytest.mark.asyncio +async def test_passthrough_without_inbound_token_is_a_no_op(): + result = await UpstreamCredentialProvider().resolve_credentials(_SUBJECT, _spec(PassthroughConfig())) + assert isinstance(result, Ok) + assert isinstance(result.ok, NoOpAuth) + + _STUBBED = [ ("api_key_byok", ApiKeyConfig(key_source=Byok())), - ("passthrough", PassthroughConfig()), ("client_credentials", ClientCredentialsConfig()), ("aws_sigv4", AwsSigV4Config(region="us-east-1")), ] diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index 6fa69b2f96c..a7a0379c7ad 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -1267,6 +1267,229 @@ class TestMCPServerManager: assert captured_extra_headers == {"Authorization": "Bearer upstream-oauth-bearer"} + async def _capture_call_extra_headers(self, server, oauth2_headers, raw_headers, user_api_key_auth): + manager = MCPServerManager() + mock_client = AsyncMock() + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) + captured = {"extra_headers": "unset"} + + async def capture_create_mcp_client( + server, mcp_auth_header, extra_headers, stdio_env, subject_token=None, **kwargs + ): # pragma: no cover - helper + captured["extra_headers"] = extra_headers + return mock_client + + manager._create_mcp_client = AsyncMock(side_effect=capture_create_mcp_client) + await manager._call_regular_mcp_tool( + mcp_server=server, + original_tool_name="tool", + arguments={}, + tasks=[], + mcp_auth_header=None, + mcp_server_auth_headers=None, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + proxy_logging_obj=None, + user_api_key_auth=user_api_key_auth, + ) + return captured["extra_headers"] + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_true_passthrough_forwards_authorization(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="server-true-passthrough", + name="tp-server", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer upstream-token"}, + raw_headers={"authorization": "Bearer upstream-token"}, + user_api_key_auth=UserAPIKeyAuth(api_key=None), + ) + assert extra_headers == {"Authorization": "Bearer upstream-token"} + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_oauth_delegate_forwards_separate_authorization(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="server-oauth-delegate", + name="od-server", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer upstream-token"}, + raw_headers={ + "x-litellm-api-key": "Bearer sk-litellm-key", + "authorization": "Bearer upstream-token", + }, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + assert extra_headers == {"Authorization": "Bearer upstream-token"} + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_oauth_delegate_never_forwards_admission_key(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="server-oauth-delegate-leak", + name="od-server", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer sk-litellm-key"}, + raw_headers={"authorization": "Bearer sk-litellm-key"}, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + def test_should_strip_caller_authorization_new_modes(self): + from litellm.proxy._types import UserAPIKeyAuth + + true_passthrough = MCPServer( + server_id="tp", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + assert ( + _should_strip_caller_authorization( + mcp_server=true_passthrough, + raw_headers={"authorization": "Bearer upstream"}, + user_api_key_auth=UserAPIKeyAuth(api_key=None), + ) + is False + ) + + oauth_delegate = MCPServer( + server_id="od", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={ + "x-litellm-api-key": "Bearer sk-litellm-key", + "authorization": "Bearer upstream", + }, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + is False + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={"authorization": "Bearer sk-litellm-key"}, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + is True + ) + + def test_should_strip_authorization_for_oauth_delegate_admitted_via_jwt_without_api_key(self): + """JWT / SSO / OIDC / session admission yields a UserAPIKeyAuth with a user_id but + api_key=None; the caller's Authorization was that credential and must be stripped for + oauth_delegate when no separate x-litellm-api-key carried admission (LIT-3794-class leak).""" + from litellm.proxy._types import UserAPIKeyAuth + + oauth_delegate = MCPServer( + server_id="od-jwt", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={"authorization": "Bearer eyJ-idp-jwt"}, + user_api_key_auth=UserAPIKeyAuth(user_id="alice", api_key=None), + ) + is True + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={ + "x-litellm-api-key": "Bearer sk-1234", + "authorization": "Bearer upstream", + }, + user_api_key_auth=UserAPIKeyAuth(user_id="alice", api_key=None), + ) + is False + ) + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_oauth_delegate_never_forwards_jwt_admission(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="od-jwt-e2e", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer eyJ-idp-jwt"}, + raw_headers={"authorization": "Bearer eyJ-idp-jwt"}, + user_api_key_auth=UserAPIKeyAuth(user_id="alice", api_key=None), + ) + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + def test_new_passthrough_modes_require_per_user_auth(self): + for auth_type in (MCPAuth.true_passthrough, MCPAuth.oauth_delegate): + server = MCPServer( + server_id="s", + name="s", + url="https://example.com", + transport=MCPTransport.http, + auth_type=auth_type, + ) + assert server.requires_per_user_auth is True + + @pytest.mark.asyncio + async def test_create_mcp_client_forwarded_modes_use_the_passthrough_arm(self): + manager = MCPServerManager() + server = MCPServer( + server_id="tp-egress", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + new_callable=AsyncMock, + ) as mock_resolve, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + ): + await manager._create_mcp_client(server=server, extra_headers={"Authorization": "Bearer upstream-token"}) + mock_resolve.assert_not_awaited() + kwargs = mock_client_cls.call_args.kwargs + emitted = httpx.Request("GET", "https://example.com/mcp") + flow = kwargs["resolved_auth"].auth_flow(emitted) + next(flow) + flow.close() + assert emitted.headers["Authorization"] == "Bearer upstream-token" + assert not kwargs["extra_headers"] or "authorization" not in {k.lower() for k in kwargs["extra_headers"]} + @pytest.mark.asyncio async def test_get_prompts_from_server_success(self): """Ensure prompts are fetched and prefixed when requested.""" diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index d9bba85bf4b..9765775af8f 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -27008,7 +27008,7 @@ export interface components { /** Alias */ alias?: string | null; /** Auth Type */ - auth_type?: ("none" | "api_key" | "bearer_token" | "basic" | "authorization" | "oauth2" | "aws_sigv4" | "token" | "oauth2_token_exchange") | null; + auth_type?: ("none" | "api_key" | "bearer_token" | "basic" | "authorization" | "oauth2" | "aws_sigv4" | "token" | "oauth2_token_exchange" | "true_passthrough" | "oauth_delegate") | null; /** Mcp Info */ mcp_info?: { [key: string]: unknown; From 446a6e8cddc883f058023ec3a173115d87437b27 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Mon, 6 Jul 2026 13:23:28 -0700 Subject: [PATCH 002/399] fix(mcp): route true_passthrough and oauth_delegate through upstream OAuth discovery Both modes advertised LiteLLM as the authorization server and answered initialize locally, so a client with no token connected empty and was never driven into the upstream OAuth flow. The protected-resource discovery now proxies the upstream metadata for both modes (verbatim for true_passthrough, resource rewritten to the gateway for oauth_delegate), and the preemptive 401 emits the matching challenge: oauth_delegate uses the gateway-proxied resource_metadata once admission passes, true_passthrough probes the upstream anonymously and surfaces its WWW-Authenticate verbatim so the client authorizes directly against the upstream --- .../mcp_server/discoverable_endpoints.py | 9 +- .../proxy/_experimental/mcp_server/server.py | 27 +- .../mcp_server/test_mcp_oauth_passthrough.py | 99 ++++++ .../mcp_server/test_mcp_stale_session.py | 310 ++++++++++++++++++ 4 files changed, 440 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 89d645b6f8a..f383c99c563 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -1337,7 +1337,9 @@ async def _build_oauth_protected_resource_response( # Pass-through branch: proxy the upstream's own metadata so discovery # directs the client at the real IdP (Okta, Keycloak, …) instead of us. - if mcp_server is not None and mcp_server.is_oauth_passthrough: + if mcp_server is not None and ( + mcp_server.is_oauth_passthrough or mcp_server.is_oauth_delegate or mcp_server.is_true_passthrough + ): try: upstream_metadata = await fetch_upstream_oauth_protected_resource(mcp_server) except Exception as exc: @@ -1353,8 +1355,9 @@ async def _build_oauth_protected_resource_response( ) if upstream_metadata is not None: - response = {**upstream_metadata, "resource": resource_url} - return response + if mcp_server.is_true_passthrough: + return upstream_metadata + return {**upstream_metadata, "resource": resource_url} # Upstream responded but with non-200 or non-dict payload. For # pass-through servers the gateway is NOT the authorization server, diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 96fd014fbe1..7a854f698c9 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -3526,6 +3526,29 @@ if MCP_AVAILABLE: headers={"www-authenticate": www_authenticate}, ) + if server and server.is_oauth_delegate and _get_forwarded_auth_from_scope(scope) is None: + www_authenticate = _get_passthrough_www_authenticate( + scope=scope, + server_name=server_name, + ) + raise HTTPException( + status_code=401, + detail="Unauthorized", + headers={"www-authenticate": www_authenticate}, + ) + + if server and server.is_true_passthrough and not _scope_has_authorization_header(scope): + upstream_status, upstream_www_authenticate = await _probe_upstream_auth(server.url or "", "") + if upstream_status == 401 and upstream_www_authenticate: + raise HTTPException( + status_code=401, + detail="Unauthorized", + headers={"www-authenticate": upstream_www_authenticate}, + ) + + def _scope_has_authorization_header(scope: Scope) -> bool: + return any(key.lower() == b"authorization" for key, _ in scope.get("headers", [])) + def _get_forwarded_auth_from_scope(scope: Scope) -> Optional[str]: """Return the upstream-bound ``Authorization`` header value, or None. @@ -3553,7 +3576,7 @@ if MCP_AVAILABLE: url: str, auth_header: str, timeout: float = 5.0, - ) -> tuple: + ) -> tuple[int, Optional[str]]: """JSON-RPC initialize-probe the upstream URL to check whether the token is accepted. Uses POST so StreamableHTTP MCP servers run the same auth path as a @@ -3583,8 +3606,8 @@ if MCP_AVAILABLE: }, } probe_headers = { - "Authorization": auth_header, "Accept": "application/json, text/event-stream", + **({"Authorization": auth_header} if auth_header else {}), } try: resp = await client.post( diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py index ad78609ee18..002e98034c1 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py @@ -472,3 +472,102 @@ async def test_oauth_protected_resource_gateway_managed_unchanged(): "https://gateway.example.com/keycloak_whoami" ] assert result["scopes_supported"] == ["read"] + + +def _make_upstream_metadata_client() -> tuple[dict, MagicMock]: + upstream_payload = { + "resource": "https://upstream.example.com/mcp", + "authorization_servers": ["https://okta.example.com/oauth2/default"], + "scopes_supported": ["openid", "profile"], + "bearer_methods_supported": ["header"], + } + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = upstream_payload + mock_client = MagicMock() + mock_client.get = AsyncMock(return_value=mock_response) + return upstream_payload, mock_client + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_oauth_delegate_proxies_upstream_with_gateway_resource(): + """oauth_delegate discovery must proxy the upstream's authorization_servers + (so the client authorizes against the upstream IdP) while rewriting resource + to the gateway (so bearers are presented back to LiteLLM). A regression that + dropped oauth_delegate from the pass-through predicate would fall through to + the gateway-AS branch and advertise LiteLLM as the authorization server.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + delegate_server = MCPServer( + server_id="delegate-1", + name="sample_docs", + server_name="sample_docs", + alias="sample_docs", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + global_mcp_server_manager.registry[delegate_server.server_id] = delegate_server + + _, mock_client = _make_upstream_metadata_client() + try: + with patch.object( + discoverable_endpoints, "get_async_httpx_client", return_value=mock_client + ): + result = await _build_oauth_protected_resource_response( + request=_make_request(), + mcp_server_name="sample_docs", + use_standard_pattern=True, + ) + + assert result["authorization_servers"] == [ + "https://okta.example.com/oauth2/default" + ] + assert result["resource"].endswith("/mcp/sample_docs") + assert result["resource"] != "https://upstream.example.com/mcp" + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_true_passthrough_returns_upstream_metadata_verbatim(): + """true_passthrough discovery must return the upstream metadata verbatim, + resource included, so the client treats the upstream as the resource and + authorizes directly against it. A regression that rewrote resource (the + gateway-proxied behavior) would break the transparent-proxy contract.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + true_passthrough_server = MCPServer( + server_id="tp-1", + name="sample_docs", + server_name="sample_docs", + alias="sample_docs", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + global_mcp_server_manager.registry[true_passthrough_server.server_id] = ( + true_passthrough_server + ) + + upstream_payload, mock_client = _make_upstream_metadata_client() + try: + with patch.object( + discoverable_endpoints, "get_async_httpx_client", return_value=mock_client + ): + result = await _build_oauth_protected_resource_response( + request=_make_request(), + mcp_server_name="sample_docs", + use_standard_pattern=True, + ) + + assert result == upstream_payload + assert result["resource"] == "https://upstream.example.com/mcp" + finally: + global_mcp_server_manager.registry.clear() diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py index 76614a35b53..44ea5f43a70 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py @@ -713,6 +713,9 @@ async def test_handle_streamable_http_mcp_delegated_server_surfaces_upstream_cha delegated_server.auth_type = MCPAuth.oauth2 delegated_server.delegate_auth_to_upstream = True delegated_server.needs_user_oauth_token = True + delegated_server.is_oauth_passthrough = False + delegated_server.is_oauth_delegate = False + delegated_server.is_true_passthrough = False delegated_server.server_id = "delegated-oauth-server" upstream_challenge = 'Bearer resource_metadata="https://upstream.example.com/.well-known/oauth-protected-resource"' @@ -1048,3 +1051,310 @@ async def test_handle_streamable_http_mcp_token_exchange_without_subject_returns assert "/.well-known/oauth-protected-resource" in challenge assert challenge.split('resource_metadata="', 1)[1].split('"', 1)[0].endswith("/mcp/obo_server") assert 'error="invalid_token"' in challenge + + +def _passthrough_mode_scope(server_name: str, extra_headers=None): + headers = [ + (b"content-type", b"application/json"), + (b"host", b"litellm.example.com"), + ] + list(extra_headers or []) + return { + "type": "http", + "method": "POST", + "path": f"/mcp/{server_name}", + "_original_path": f"/{server_name}/mcp", + "scheme": "https", + "query_string": b"", + "root_path": "", + "server": ("litellm.example.com", 443), + "headers": headers, + } + + +def _build_passthrough_mode_server(server_name: str, auth_type): + from litellm.proxy._types import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + return MCPServer( + server_id=f"{server_name}-id", + name=server_name, + server_name=server_name, + alias=server_name, + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=auth_type, + ) + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_oauth_delegate_without_token_returns_gateway_proxied_401(): + """oauth_delegate is admitted with the LiteLLM key but still owns upstream + OAuth. With no forwarded upstream token the gateway must challenge with the + proxied resource_metadata (which advertises the upstream IdP), never the + gateway authorization_uri and never a silent 200.""" + from fastapi import HTTPException + + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateful, + ) + except ImportError: + pytest.skip("MCP server not available") + + scope = _passthrough_mode_scope("od_server") + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = "u1" + od_server = _build_passthrough_mode_server("od_server", MCPAuth.oauth_delegate) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["od_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=od_server, + ), + patch.object( + session_manager_stateful, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 0 + assert exc_info.value.status_code == 401 + challenge = exc_info.value.headers["www-authenticate"] + assert "resource_metadata=" in challenge + assert "authorization_uri=" not in challenge + assert "/.well-known/oauth-protected-resource/od_server/mcp" in challenge + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_oauth_delegate_with_forwarded_token_skips_challenge(): + """When the oauth_delegate caller carries both the LiteLLM key and a separate + upstream Authorization, the gateway must forward to the session manager, not + re-challenge. Guards the ``_get_forwarded_auth_from_scope(...) is None`` + condition: dropping it would 401 even a fully-authenticated request.""" + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateless, + ) + except ImportError: + pytest.skip("MCP server not available") + + scope = _passthrough_mode_scope( + "od_server", + extra_headers=[ + (b"x-litellm-api-key", b"Bearer sk-1234"), + (b"authorization", b"Bearer upstream-token"), + ], + ) + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = "u1" + od_server = _build_passthrough_mode_server("od_server", MCPAuth.oauth_delegate) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["od_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._check_passthrough_upstream_auth", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=od_server, + ), + patch.object( + session_manager_stateless, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + patch.object(session_manager_stateless, "_server_instances", {}), + ): + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 1 + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_true_passthrough_without_token_surfaces_verbatim_upstream_challenge(): + """true_passthrough is a transparent proxy: with no client Authorization the + gateway probes the upstream and surfaces its own WWW-Authenticate verbatim, + so the client discovers and authorizes against the upstream directly. Guards + against answering initialize locally with a silent 200.""" + from fastapi import HTTPException + + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateful, + ) + except ImportError: + pytest.skip("MCP server not available") + + upstream_challenge = 'Bearer resource_metadata="https://upstream.example.com/.well-known/oauth-protected-resource"' + probe_response = MagicMock() + probe_response.status_code = 401 + probe_response.headers = {"www-authenticate": upstream_challenge} + probe_client = MagicMock() + probe_client.post = AsyncMock(return_value=probe_response) + + scope = _passthrough_mode_scope("tp_server") + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = None + tp_server = _build_passthrough_mode_server("tp_server", MCPAuth.true_passthrough) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["tp_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", + return_value=probe_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=tp_server, + ), + patch.object( + session_manager_stateful, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 0 + assert exc_info.value.status_code == 401 + assert exc_info.value.headers["www-authenticate"] == upstream_challenge + probe_client.post.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_true_passthrough_with_token_skips_probe_and_challenge(): + """When the true_passthrough caller already carries an Authorization the + gateway must forward without probing or challenging. Guards the + ``not _scope_has_authorization_header(scope)`` condition and the no-probe + fast path.""" + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateless, + ) + except ImportError: + pytest.skip("MCP server not available") + + probe_client = MagicMock() + probe_client.post = AsyncMock() + + scope = _passthrough_mode_scope( + "tp_server", + extra_headers=[(b"authorization", b"Bearer upstream-token")], + ) + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = None + tp_server = _build_passthrough_mode_server("tp_server", MCPAuth.true_passthrough) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["tp_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._check_passthrough_upstream_auth", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", + return_value=probe_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=tp_server, + ), + patch.object( + session_manager_stateless, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + patch.object(session_manager_stateless, "_server_instances", {}), + ): + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 1 + probe_client.post.assert_not_awaited() From 9bf3907de546beb34bbfddad048c8295e746e50a Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Tue, 7 Jul 2026 16:13:43 -0700 Subject: [PATCH 003/399] fix(mcp): bind oauth_delegate discovery resource to the upstream oauth_delegate forwards the caller's token to the upstream, which validates its audience, so the protected-resource metadata must keep resource pointing at the upstream (returned verbatim, like true_passthrough) rather than rewriting it to the gateway. Rewriting to the gateway asks the client to mint a token bound to the gateway audience, which a strict IdP (Entra) refuses to issue for an unregistered resource and a spec-compliant upstream rejects on receipt. The legacy is_oauth_passthrough opt-in keeps the gateway rewrite unchanged. --- .../mcp_server/discoverable_endpoints.py | 16 ++-- .../mcp_server/test_mcp_oauth_passthrough.py | 94 ++++++------------- 2 files changed, 39 insertions(+), 71 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index f383c99c563..415336b1a9e 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -1292,11 +1292,15 @@ async def _build_oauth_protected_resource_response( """ Build OAuth protected resource response with the appropriate URL pattern. - For pass-through MCP servers (``MCPServer.is_oauth_passthrough``), the - gateway proxies the upstream's own ``oauth-protected-resource`` metadata - so that standards-compliant MCP clients discover the **upstream** IdP - instead of the gateway. The ``resource`` field is rewritten to the - gateway's own URL so clients present the bearer token back to the gateway. + For pass-through MCP servers, the gateway proxies the upstream's own + ``oauth-protected-resource`` metadata so standards-compliant MCP clients + discover the **upstream** IdP instead of the gateway. For ``true_passthrough`` + and ``oauth_delegate`` the metadata is returned verbatim (``resource`` stays + the upstream): the caller's token is forwarded to and validated by the + upstream, so its audience must be the upstream — rewriting it to the gateway + would make a strict IdP (e.g. Entra) refuse to mint it or the upstream reject + it. Only the legacy ``is_oauth_passthrough`` opt-in rewrites ``resource`` to + the gateway's own URL so clients present the bearer token back to the gateway. Args: request: FastAPI Request object @@ -1355,7 +1359,7 @@ async def _build_oauth_protected_resource_response( ) if upstream_metadata is not None: - if mcp_server.is_true_passthrough: + if mcp_server.is_true_passthrough or mcp_server.is_oauth_delegate: return upstream_metadata return {**upstream_metadata, "resource": resource_url} diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py index 002e98034c1..ef2a8318d7a 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py @@ -34,8 +34,7 @@ from litellm.types.mcp_server.mcp_server_manager import MCPServer def _mock_mcp_client_ip(): """Bypass IP-based access control in tests.""" with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints" - ".IPAddressUtils.get_mcp_client_ip", + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.IPAddressUtils.get_mcp_client_ip", return_value=None, ): yield @@ -191,9 +190,7 @@ async def test_oauth_protected_resource_passthrough_proxies_upstream_metadata(): extra_headers=["Authorization"], oauth_passthrough=True, ) - global_mcp_server_manager.registry[passthrough_server.server_id] = ( - passthrough_server - ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server upstream_payload = { "resource": "https://upstream.example.com/mcp", @@ -207,18 +204,14 @@ async def test_oauth_protected_resource_passthrough_proxies_upstream_metadata(): mock_client = MagicMock() mock_client.get = AsyncMock(return_value=mock_response) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): result = await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="sample_docs", use_standard_pattern=True, ) - assert result["authorization_servers"] == [ - "https://okta.example.com/oauth2/default" - ] + assert result["authorization_servers"] == ["https://okta.example.com/oauth2/default"] # resource is normalized to the gateway URL so bearers are sent back to us assert result["resource"].endswith("/mcp/sample_docs") assert result["scopes_supported"] == ["openid", "profile"] @@ -242,9 +235,7 @@ async def test_oauth_protected_resource_passthrough_cache_hit(): extra_headers=["Authorization"], oauth_passthrough=True, ) - global_mcp_server_manager.registry[passthrough_server.server_id] = ( - passthrough_server - ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server mock_response = MagicMock() mock_response.status_code = 200 @@ -254,9 +245,7 @@ async def test_oauth_protected_resource_passthrough_cache_hit(): mock_client = MagicMock() mock_client.get = AsyncMock(return_value=mock_response) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="sample_docs", @@ -348,12 +337,8 @@ async def test_oauth_metadata_cache_expired_entry_is_refetched(): mock_client = MagicMock() mock_client.get = AsyncMock(return_value=mock_response) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): - result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource( - passthrough_server - ) + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): + result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource(passthrough_server) assert result == {"authorization_servers": ["https://fresh.example.com"]} assert mock_client.get.await_count == 1 @@ -377,16 +362,12 @@ async def test_oauth_protected_resource_passthrough_network_error_returns_502(): extra_headers=["Authorization"], oauth_passthrough=True, ) - global_mcp_server_manager.registry[passthrough_server.server_id] = ( - passthrough_server - ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server mock_client = MagicMock() mock_client.get = AsyncMock(side_effect=httpx.ConnectError("boom")) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): with pytest.raises(HTTPException) as exc_info: await _build_oauth_protected_resource_response( request=_make_request(), @@ -414,16 +395,10 @@ async def test_fetch_upstream_metadata_returns_none_when_not_all_candidates_netw not_found_response = MagicMock() not_found_response.status_code = 404 mock_client = MagicMock() - mock_client.get = AsyncMock( - side_effect=[not_found_response, httpx.ConnectError("path fallback failed")] - ) + mock_client.get = AsyncMock(side_effect=[not_found_response, httpx.ConnectError("path fallback failed")]) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): - result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource( - passthrough_server - ) + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): + result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource(passthrough_server) assert result is None assert mock_client.get.await_count == 2 @@ -458,9 +433,7 @@ async def test_oauth_protected_resource_gateway_managed_unchanged(): mock_client = MagicMock() mock_client.get = AsyncMock() - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): result = await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="keycloak_whoami", @@ -468,9 +441,7 @@ async def test_oauth_protected_resource_gateway_managed_unchanged(): ) mock_client.get.assert_not_awaited() - assert result["authorization_servers"] == [ - "https://gateway.example.com/keycloak_whoami" - ] + assert result["authorization_servers"] == ["https://gateway.example.com/keycloak_whoami"] assert result["scopes_supported"] == ["read"] @@ -490,12 +461,13 @@ def _make_upstream_metadata_client() -> tuple[dict, MagicMock]: @pytest.mark.asyncio -async def test_oauth_protected_resource_oauth_delegate_proxies_upstream_with_gateway_resource(): - """oauth_delegate discovery must proxy the upstream's authorization_servers - (so the client authorizes against the upstream IdP) while rewriting resource - to the gateway (so bearers are presented back to LiteLLM). A regression that - dropped oauth_delegate from the pass-through predicate would fall through to - the gateway-AS branch and advertise LiteLLM as the authorization server.""" +async def test_oauth_protected_resource_oauth_delegate_returns_upstream_metadata_verbatim(): + """oauth_delegate discovery must return the upstream metadata verbatim, + resource included. The caller's token is forwarded to and validated by the + upstream, so its audience must be the upstream; rewriting resource to the + gateway would make a strict IdP refuse to mint it or the upstream reject it. + A regression that dropped oauth_delegate from the pass-through predicate would + fall through to the gateway-AS branch and advertise LiteLLM as the AS.""" from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( global_mcp_server_manager, ) @@ -512,22 +484,18 @@ async def test_oauth_protected_resource_oauth_delegate_proxies_upstream_with_gat ) global_mcp_server_manager.registry[delegate_server.server_id] = delegate_server - _, mock_client = _make_upstream_metadata_client() + upstream_payload, mock_client = _make_upstream_metadata_client() try: - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): result = await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="sample_docs", use_standard_pattern=True, ) - assert result["authorization_servers"] == [ - "https://okta.example.com/oauth2/default" - ] - assert result["resource"].endswith("/mcp/sample_docs") - assert result["resource"] != "https://upstream.example.com/mcp" + assert result == upstream_payload + assert result["authorization_servers"] == ["https://okta.example.com/oauth2/default"] + assert result["resource"] == "https://upstream.example.com/mcp" finally: global_mcp_server_manager.registry.clear() @@ -552,15 +520,11 @@ async def test_oauth_protected_resource_true_passthrough_returns_upstream_metada transport=MCPTransport.http, auth_type=MCPAuth.true_passthrough, ) - global_mcp_server_manager.registry[true_passthrough_server.server_id] = ( - true_passthrough_server - ) + global_mcp_server_manager.registry[true_passthrough_server.server_id] = true_passthrough_server upstream_payload, mock_client = _make_upstream_metadata_client() try: - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): result = await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="sample_docs", From 732832d3426a1a0c29363e83d62be0546d338d8f Mon Sep 17 00:00:00 2001 From: Tin Date: Tue, 7 Jul 2026 19:46:23 -0700 Subject: [PATCH 004/399] fix(mcp): bind client-forwarded Authorization to a single upstream In a listing fan-out over a scope containing more than one server that consumes the caller's Authorization (true_passthrough, oauth_delegate, or the legacy delegate/passthrough shapes), the request-wide bearer is now withheld from the new modes instead of being replayed against every upstream (RFC 9700 cross-resource replay). Explicitly-addressed operations (tool call, get_prompt, read_resource, single-server routes) keep forwarding it. Multi-server aggregates use the per-server x-mcp-{alias}-authorization header instead: its value now feeds the passthrough resolver arm as the inbound token and wins over the request-wide header, binding one token to one server. --- .../mcp_server/mcp_server_manager.py | 51 ++ .../proxy/_experimental/mcp_server/server.py | 28 +- .../mcp_server/test_mcp_server.py | 583 +++++++----------- .../mcp_server/test_mcp_server_manager.py | 112 ++++ 4 files changed, 400 insertions(+), 374 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index fd978e5bfc9..acb7c80c5b7 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -368,6 +368,54 @@ def _take_forwarded_authorization( return value, _without_authorization(headers) +def _passthrough_token_from_mcp_auth_header( + mcp_auth_header: Optional[Union[str, dict[str, str]]], +) -> Optional[str]: + """The caller's per-server upstream credential for a passthrough-mode server, or None. + + Sourced from ``x-mcp-{alias}-authorization`` (string or per-header dict form) or the deprecated + global ``x-mcp-auth`` fallback. Per-server headers are the multi-server shape: they bind one + token to one server, so an aggregate scope with several passthrough-mode servers never replays + a single credential across upstreams. The value is forwarded verbatim, so it must be the full + header value (e.g. ``Bearer ``).""" + if isinstance(mcp_auth_header, str): + return mcp_auth_header or None + if isinstance(mcp_auth_header, dict): + return next((v for k, v in mcp_auth_header.items() if k.lower() == "authorization"), None) + return None + + +def _consumes_caller_authorization(server: MCPServer) -> bool: + """True when this server's egress forwards the caller's request-wide ``Authorization`` upstream: + the client-forwarded token modes, legacy OAuth pass-through, and legacy upstream-delegated + interactive oauth2. An unstamped oauth2 row (flow column not yet backfilled) reads as a consumer, + which errs toward suppression — the fail-safe direction.""" + if server.is_true_passthrough or server.is_oauth_delegate or server.is_oauth_passthrough: + return True + return ( + server.auth_type == MCPAuth.oauth2 + and getattr(server, "delegate_auth_to_upstream", False) is True + and not server.has_client_credentials + ) + + +def _caller_authorization_fans_out( + server: MCPServer, + scope_servers: Optional[list[MCPServer]], +) -> bool: + """True when forwarding the caller's request-wide ``Authorization`` to ``server`` inside a + listing fan-out would replay one credential against multiple upstreams: another server in the + scope also consumes it (RFC 9700 cross-resource replay). ``scope_servers`` is None for + explicitly-addressed operations (tool call, get_prompt, read_resource, single-server routes), + where the client named the one target and the gateway is not choosing recipients.""" + if scope_servers is None: + return False + return any( + other is not None and other.server_id != server.server_id and _consumes_caller_authorization(other) + for other in scope_servers + ) + + def _extract_upstream_auth_failure( exc: BaseException, ) -> Optional[tuple[int, Optional[str]]]: @@ -2357,6 +2405,9 @@ class MCPServerManager: inbound_token = subject_token if isinstance(spec.config, PassthroughConfig): inbound_token, extra_headers = _take_forwarded_authorization(extra_headers) + per_server_token = _passthrough_token_from_mcp_auth_header(mcp_auth_header) + if per_server_token is not None: + inbound_token = per_server_token resolved_auth, extra_headers = await self._resolve_v2_auth( server=server, spec=spec, diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 7a854f698c9..4fda15f664e 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -331,6 +331,7 @@ if MCP_AVAILABLE: ) from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( MCPServerManager, + _caller_authorization_fans_out, _client_forwarded_authorization_headers, _should_strip_caller_authorization, _without_authorization, @@ -1529,8 +1530,16 @@ if MCP_AVAILABLE: oauth2_headers: Optional[Dict[str, str]], raw_headers: Optional[Dict[str, str]], user_api_key_auth: Optional[UserAPIKeyAuth] = None, + scope_servers: Optional[list[MCPServer]] = None, ) -> Tuple[Optional[Union[Dict[str, str], str]], Optional[Dict[str, str]]]: - """Build auth and extra headers for a server.""" + """Build auth and extra headers for a server. + + ``scope_servers`` is the full server list a fan-out handler iterates. Passing it lets the + client-forwarded token modes withhold the caller's request-wide ``Authorization`` when + another server in the scope would also receive it (``_caller_authorization_fans_out``); + explicitly-addressed operations leave it None. Per-server ``x-mcp-{alias}-authorization`` + headers are unaffected — they bind one token to one server and are the multi-server shape. + """ server_auth_header: Optional[Union[Dict[str, str], str]] = None if mcp_server_auth_headers: from litellm.proxy._experimental.mcp_server.utils import ( @@ -1563,12 +1572,13 @@ if MCP_AVAILABLE: ): extra_headers = _without_authorization(extra_headers) elif server.is_true_passthrough or server.is_oauth_delegate: - extra_headers = _client_forwarded_authorization_headers( - mcp_server=server, - oauth2_headers=oauth2_headers, - raw_headers=raw_headers, - user_api_key_auth=user_api_key_auth, - ) + if not _caller_authorization_fans_out(server, scope_servers): + extra_headers = _client_forwarded_authorization_headers( + mcp_server=server, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) if server.extra_headers and raw_headers: if extra_headers is None: @@ -1793,6 +1803,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) # Prefer server-stored per-user OAuth when configured, so a stale @@ -1979,6 +1990,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) try: @@ -2031,6 +2043,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) try: @@ -2081,6 +2094,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) try: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index b24457deabd..71ece75b2fb 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -69,9 +69,7 @@ async def test_mcp_server_tool_call_body_contains_request_data(): # Mock the add_litellm_data_to_request function to capture the data captured_data = {} - async def mock_add_litellm_data_to_request( - data, request, user_api_key_dict, proxy_config - ): + async def mock_add_litellm_data_to_request(data, request, user_api_key_dict, proxy_config): captured_data.update(data) # Simulate the proxy_server_request creation captured_data["proxy_server_request"] = { @@ -355,6 +353,79 @@ def test_prepare_mcp_server_headers_m2m_skips_authorization_from_raw_extra_heade assert extra_headers.get("X-Custom") == "trace" +def _client_forwarded_mode_server(server_id: str, auth_type) -> MCPServer: + return MCPServer( + server_id=server_id, + name=server_id, + transport=MCPTransport.http, + auth_type=auth_type, + ) + + +def _prepare_headers_in_scope(server: MCPServer, scope_servers): + from litellm.proxy._experimental.mcp_server.server import ( + _prepare_mcp_server_headers, + ) + + return _prepare_mcp_server_headers( + server=server, + mcp_server_auth_headers=None, + mcp_auth_header=None, + oauth2_headers={"Authorization": "Bearer upstream-token"}, + raw_headers={ + "x-litellm-api-key": "Bearer sk-litellm-key", + "authorization": "Bearer upstream-token", + }, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + scope_servers=scope_servers, + ) + + +def test_prepare_mcp_server_headers_withholds_global_authorization_when_scope_fans_out(): + """One caller bearer must not be replayed against multiple upstreams (RFC 9700 + cross-resource replay): in a fan-out scope with a second Authorization-consuming + server, the client-forwarded modes get no global Authorization.""" + delegate = _client_forwarded_mode_server("od-fanout", MCPAuth.oauth_delegate) + second_consumer = _client_forwarded_mode_server("tp-fanout", MCPAuth.true_passthrough) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, second_consumer]) + + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + +def test_prepare_mcp_server_headers_forwards_global_authorization_to_sole_consumer(): + """Non-consuming servers (static api_key) in scope do not make the forward ambiguous.""" + delegate = _client_forwarded_mode_server("od-sole", MCPAuth.oauth_delegate) + static_server = MCPServer( + server_id="static-api-key", + name="static-api-key", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="static-key", + ) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, static_server]) + + assert extra_headers == {"Authorization": "Bearer upstream-token"} + + +def test_prepare_mcp_server_headers_scope_counts_legacy_delegate_as_consumer(): + """Legacy upstream-delegated oauth2 servers still receive the caller's Authorization on the + v1 path, so their presence in scope must suppress the new modes' forward too.""" + delegate = _client_forwarded_mode_server("od-vs-legacy", MCPAuth.oauth_delegate) + legacy_delegate = MCPServer( + server_id="legacy-delegate", + name="legacy-delegate", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + ) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, legacy_delegate]) + + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + @pytest.mark.asyncio async def test_call_tool_m2m_skips_authorization_headers(): """M2M call_tool must not forward caller Authorization in oauth2/raw headers.""" @@ -382,9 +453,7 @@ async def test_call_tool_m2m_skips_authorization_headers(): mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) - with patch.object( - manager, "_create_mcp_client", new=AsyncMock(return_value=mock_client) - ) as create_client_mock: + with patch.object(manager, "_create_mcp_client", new=AsyncMock(return_value=mock_client)) as create_client_mock: await manager._call_regular_mcp_tool( mcp_server=server, original_tool_name="echo", @@ -879,9 +948,7 @@ async def test_get_tools_from_mcp_servers_continues_when_one_server_fails(): # Mock global_mcp_server_manager mock_manager = MagicMock() - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["working_server", "failing_server"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["working_server", "failing_server"]) mock_manager.get_mcp_server_by_id = lambda server_id: ( working_server if server_id == "working_server" else failing_server ) @@ -942,9 +1009,7 @@ async def test_get_tools_from_mcp_servers_continues_when_one_server_fails(): ) # Verify success logging - mock_logger.info.assert_any_call( - "Successfully fetched 1 tools total from all MCP servers" - ) + mock_logger.info.assert_any_call("Successfully fetched 1 tools total from all MCP servers") @pytest.mark.asyncio @@ -985,9 +1050,7 @@ async def test_get_tools_from_mcp_servers_handles_all_servers_failing(): # Mock global_mcp_server_manager mock_manager = MagicMock() - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["failing_server1", "failing_server2"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["failing_server1", "failing_server2"]) mock_manager.get_mcp_server_by_id = lambda server_id: ( failing_server1 if server_id == "failing_server1" else failing_server2 ) @@ -1042,9 +1105,7 @@ async def test_get_tools_from_mcp_servers_handles_all_servers_failing(): ) # Verify total logging - mock_logger.info.assert_any_call( - "Successfully fetched 0 tools total from all MCP servers" - ) + mock_logger.info.assert_any_call("Successfully fetched 0 tools total from all MCP servers") @pytest.mark.asyncio @@ -1069,9 +1130,7 @@ async def test_mcp_server_tool_call_body_with_none_arguments(): # Mock the add_litellm_data_to_request function to capture the data captured_data = {} - async def mock_add_litellm_data_to_request( - data, request, user_api_key_dict, proxy_config - ): + async def mock_add_litellm_data_to_request(data, request, user_api_key_dict, proxy_config): captured_data.update(data) captured_data["proxy_server_request"] = { "url": str(request.url), @@ -1176,31 +1235,29 @@ async def test_concurrent_initialize_session_managers(): results = await asyncio.gather(*tasks, return_exceptions=True) # All tasks should complete successfully (no exceptions) - assert all( - result == "success" for result in results - ), f"Some tasks failed: {results}" + assert all(result == "success" for result in results), f"Some tasks failed: {results}" # Each session manager.run() should only be called once due to the lock - assert ( - mock_stateless_run.call_count == 1 - ), f"Expected 1 call to session_manager_stateless.run(), got {mock_stateless_run.call_count}" - assert ( - mock_stateful_run.call_count == 1 - ), f"Expected 1 call to session_manager_stateful.run(), got {mock_stateful_run.call_count}" - assert ( - mock_sse_run.call_count == 1 - ), f"Expected 1 call to sse_session_manager.run(), got {mock_sse_run.call_count}" + assert mock_stateless_run.call_count == 1, ( + f"Expected 1 call to session_manager_stateless.run(), got {mock_stateless_run.call_count}" + ) + assert mock_stateful_run.call_count == 1, ( + f"Expected 1 call to session_manager_stateful.run(), got {mock_stateful_run.call_count}" + ) + assert mock_sse_run.call_count == 1, ( + f"Expected 1 call to sse_session_manager.run(), got {mock_sse_run.call_count}" + ) # The context managers should only be entered once each - assert ( - mock_cm_stateless.__aenter__.call_count == 1 - ), f"Expected 1 call to stateless __aenter__, got {mock_cm_stateless.__aenter__.call_count}" - assert ( - mock_cm_stateful.__aenter__.call_count == 1 - ), f"Expected 1 call to stateful __aenter__, got {mock_cm_stateful.__aenter__.call_count}" - assert ( - mock_cm_sse.__aenter__.call_count == 1 - ), f"Expected 1 call to sse __aenter__, got {mock_cm_sse.__aenter__.call_count}" + assert mock_cm_stateless.__aenter__.call_count == 1, ( + f"Expected 1 call to stateless __aenter__, got {mock_cm_stateless.__aenter__.call_count}" + ) + assert mock_cm_stateful.__aenter__.call_count == 1, ( + f"Expected 1 call to stateful __aenter__, got {mock_cm_stateful.__aenter__.call_count}" + ) + assert mock_cm_sse.__aenter__.call_count == 1, ( + f"Expected 1 call to sse __aenter__, got {mock_cm_sse.__aenter__.call_count}" + ) # State should be properly set assert mcp_server._SESSION_MANAGERS_INITIALIZED is True @@ -1343,16 +1400,12 @@ async def test_mcp_routing_initialize_to_stateful_no_session_to_stateless(): # initialize → stateful init_body = b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' stateless_called, stateful_called = await make_request(init_body) - assert ( - stateful_called and not stateless_called - ), "initialize (no session) should route to stateful, not stateless" + assert stateful_called and not stateless_called, "initialize (no session) should route to stateful, not stateless" # tools/list → stateless tools_body = b'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' stateless_called, stateful_called = await make_request(tools_body) - assert ( - stateless_called and not stateful_called - ), "tools/list (no session) should route to stateless, not stateful" + assert stateless_called and not stateful_called, "tools/list (no session) should route to stateless, not stateful" @pytest.mark.asyncio @@ -1437,9 +1490,9 @@ async def test_mcp_routing_chunked_initialize_to_stateful(): ): await handle_streamable_http_mcp(scope, receive, send) - assert ( - stateful_called and not stateless_called - ), "chunked initialize (no session) should route to stateful, not stateless" + assert stateful_called and not stateless_called, ( + "chunked initialize (no session) should route to stateful, not stateless" + ) @pytest.mark.asyncio @@ -1468,10 +1521,7 @@ async def test_mcp_routing_caps_body_peek_for_oversized_chunked_body(): messages = [ {"type": "http.request", "body": first_chunk, "more_body": True}, - *[ - {"type": "http.request", "body": chunk, "more_body": True} - for chunk in oversized_tail - ], + *[{"type": "http.request", "body": chunk, "more_body": True} for chunk in oversized_tail], {"type": "http.request", "body": b"", "more_body": False}, ] receive_calls = {"count": 0} @@ -1522,12 +1572,8 @@ async def test_mcp_routing_caps_body_peek_for_oversized_chunked_body(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateless, "handle_request", side_effect=stateless_handle - ), - patch.object( - session_manager_stateful, "handle_request", side_effect=stateful_handle - ), + patch.object(session_manager_stateless, "handle_request", side_effect=stateless_handle), + patch.object(session_manager_stateful, "handle_request", side_effect=stateful_handle), patch.object(session_manager_stateless, "_server_instances", {}), patch.object(session_manager_stateful, "_server_instances", {}), ): @@ -1578,9 +1624,7 @@ async def test_enforce_stateful_session_cap_evicts_oldest_idle_then_rejects(): patch.object(session_manager_stateful, "_server_instances", instances), patch.object(mcp_server, "_MAX_STATEFUL_SESSIONS_PER_OWNER", 3), patch.dict(mcp_server._stateful_session_owners, owners, clear=True), - patch.dict( - mcp_server._stateful_session_auth_context_last_seen, last_seen, clear=True - ), + patch.dict(mcp_server._stateful_session_auth_context_last_seen, last_seen, clear=True), patch.dict(mcp_server._stateful_session_auth_contexts, contexts, clear=True), patch.dict(mcp_server._stateful_session_active_request_counts, {}, clear=True), ): @@ -1593,9 +1637,7 @@ async def test_enforce_stateful_session_cap_evicts_oldest_idle_then_rejects(): # A different owner at the cap is unaffected by owner-A's sessions. terminated.clear() - allowed_other = await mcp_server._enforce_stateful_session_cap_for_owner( - "owner-B" - ) + allowed_other = await mcp_server._enforce_stateful_session_cap_for_owner("owner-B") assert allowed_other is True assert terminated == [] @@ -1614,9 +1656,7 @@ async def test_enforce_stateful_session_cap_evicts_oldest_idle_then_rejects(): {f"s{i}": float(i) for i in range(3)}, clear=True, ), - patch.dict( - mcp_server._stateful_session_active_request_counts, active, clear=True - ), + patch.dict(mcp_server._stateful_session_active_request_counts, active, clear=True), ): rejected = await mcp_server._enforce_stateful_session_cap_for_owner("owner-A") assert rejected is False @@ -1662,9 +1702,7 @@ async def test_mcp_routing_initialize_rejected_when_owner_at_session_cap(): (b"authorization", b"Bearer test-key"), ], } - receive = AsyncMock( - return_value={"type": "http.request", "body": init_body, "more_body": False} - ) + receive = AsyncMock(return_value={"type": "http.request", "body": init_body, "more_body": False}) send = AsyncMock() stateful_called = [] @@ -1685,9 +1723,7 @@ async def test_mcp_routing_initialize_rejected_when_owner_at_session_cap(): ), patch.object(mcp_server, "_owner_fingerprint_for", return_value="owner-X"), patch.object(mcp_server, "_MAX_STATEFUL_SESSIONS_PER_OWNER", cap), - patch.object( - session_manager_stateful, "handle_request", side_effect=stateful_handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=stateful_handle), patch.object(session_manager_stateful, "_server_instances", instances), patch.object(session_manager_stateless, "_server_instances", {}), patch.dict(mcp_server._stateful_session_owners, owners, clear=True), @@ -1696,18 +1732,14 @@ async def test_mcp_routing_initialize_rejected_when_owner_at_session_cap(): {f"s{i}": float(i) for i in range(cap)}, clear=True, ), - patch.dict( - mcp_server._stateful_session_active_request_counts, active, clear=True - ), + patch.dict(mcp_server._stateful_session_active_request_counts, active, clear=True), patch.dict(mcp_server._stateful_session_auth_contexts, contexts, clear=True), ): await handle_streamable_http_mcp(scope, receive, send) assert not stateful_called, "initialize at session cap must not reach the manager" start_messages = [ - call.args[0] - for call in send.call_args_list - if call.args and call.args[0].get("type") == "http.response.start" + call.args[0] for call in send.call_args_list if call.args and call.args[0].get("type") == "http.response.start" ] assert start_messages, "a response should have been sent" assert start_messages[0]["status"] == 429 @@ -1743,9 +1775,7 @@ async def test_stateful_mcp_requests_refresh_session_auth_context(): None, "1.1.1.1", ) - mcp_server._stateful_session_auth_contexts[session_id] = callback_context.run( - mcp_server.auth_context_var.get - ) + mcp_server._stateful_session_auth_contexts[session_id] = callback_context.run(mcp_server.auth_context_var.get) scope = { "type": "http", @@ -2006,24 +2036,14 @@ async def test_initialize_request_with_existing_session_tracks_new_session(): ) await mcp_server._purge_expired_stateful_session_auth_contexts(now=now) assert new_session_id in mcp_server._stateful_session_auth_contexts - assert ( - mcp_server._stateful_session_auth_contexts[new_session_id] - is not existing_auth_user - ) - assert ( - mcp_server._stateful_session_auth_contexts[new_session_id].mcp_auth_header - == "new-mcp-auth" - ) + assert mcp_server._stateful_session_auth_contexts[new_session_id] is not existing_auth_user + assert mcp_server._stateful_session_auth_contexts[new_session_id].mcp_auth_header == "new-mcp-auth" async def stateless_handle(s, r, se): - raise AssertionError( - "initialize request with session should use stateful manager" - ) + raise AssertionError("initialize request with session should use stateful manager") try: - mcp_server._stateful_session_auth_contexts[existing_session_id] = ( - existing_auth_user - ) + mcp_server._stateful_session_auth_contexts[existing_session_id] = existing_auth_user mcp_server._stateful_session_auth_context_last_seen[existing_session_id] = 1.0 mcp_server._stateful_session_owners[existing_session_id] = owner_fingerprint @@ -2065,10 +2085,7 @@ async def test_initialize_request_with_existing_session_tracks_new_session(): assert stateful_called assert new_session_id not in mcp_server._stateful_session_active_request_counts assert new_session_id in mcp_server._stateful_session_auth_contexts - assert ( - mcp_server._stateful_session_auth_contexts[existing_session_id] - is existing_auth_user - ) + assert mcp_server._stateful_session_auth_contexts[existing_session_id] is existing_auth_user assert existing_auth_user.mcp_auth_header == "old-mcp-auth" assert existing_auth_user.mcp_servers == ["old-server"] finally: @@ -2191,15 +2208,11 @@ async def test_stateful_mcp_cleanup_loop_survives_purge_errors(): except ImportError: pytest.skip("MCP server not available") - purge = AsyncMock( - side_effect=[RuntimeError("terminate failed"), asyncio.CancelledError()] - ) + purge = AsyncMock(side_effect=[RuntimeError("terminate failed"), asyncio.CancelledError()]) with ( patch.object(mcp_server.asyncio, "sleep", AsyncMock(return_value=None)), - patch.object( - mcp_server, "_purge_expired_stateful_session_auth_contexts", purge - ), + patch.object(mcp_server, "_purge_expired_stateful_session_auth_contexts", purge), ): with pytest.raises(asyncio.CancelledError): await mcp_server._cleanup_expired_stateful_session_auth_contexts() @@ -2289,9 +2302,7 @@ async def test_stateful_mcp_session_owner_mismatch_returns_403(): intruder_auth = UserAPIKeyAuth(api_key="intruder-key", user_id="intruder") mcp_server._stateful_session_auth_contexts[session_id] = MagicMock() - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth - ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) scope = { "type": "http", @@ -2341,9 +2352,7 @@ async def test_stateful_mcp_session_owner_mismatch_returns_403(): await handle_streamable_http_mcp(scope, receive, capture_send) handle_request_mock.assert_not_awaited() - statuses = [ - m["status"] for m in sent_messages if m.get("type") == "http.response.start" - ] + statuses = [m["status"] for m in sent_messages if m.get("type") == "http.response.start"] assert statuses == [403] mcp_server._stateful_session_auth_contexts.pop(session_id, None) @@ -2368,12 +2377,10 @@ async def test_stateful_mcp_session_serializes_concurrent_requests(): session_id = "serialized-session-1" owner_auth = UserAPIKeyAuth(api_key="owner-key", user_id="owner") - mcp_server._stateful_session_auth_contexts[session_id] = ( - mcp_server.MCPAuthenticatedUser(user_api_key_auth=owner_auth) - ) - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth + mcp_server._stateful_session_auth_contexts[session_id] = mcp_server.MCPAuthenticatedUser( + user_api_key_auth=owner_auth ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) inside = 0 max_inside = 0 @@ -2414,9 +2421,7 @@ async def test_stateful_mcp_session_serializes_concurrent_requests(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=slow_handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=slow_handle), patch.object( session_manager_stateful, "_server_instances", @@ -2432,9 +2437,7 @@ async def test_stateful_mcp_session_serializes_concurrent_requests(): mcp_server._stateful_session_owners.pop(session_id, None) mcp_server._stateful_session_locks.pop(session_id, None) - assert ( - max_inside == 1 - ), "concurrent requests on same stateful session must be serialized" + assert max_inside == 1, "concurrent requests on same stateful session must be serialized" @pytest.mark.asyncio @@ -2486,9 +2489,7 @@ async def test_stateful_mcp_lock_does_not_leak_when_auth_context_missing(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=handle), patch.object( session_manager_stateful, "_server_instances", @@ -2498,9 +2499,9 @@ async def test_stateful_mcp_lock_does_not_leak_when_auth_context_missing(): assert session_id not in mcp_server._stateful_session_auth_contexts await handle_streamable_http_mcp(scope, receive, AsyncMock()) - assert ( - session_id not in mcp_server._stateful_session_locks - ), "lock entry must be cleaned up for untracked stateful session" + assert session_id not in mcp_server._stateful_session_locks, ( + "lock entry must be cleaned up for untracked stateful session" + ) finally: mcp_server._stateful_session_auth_contexts.pop(session_id, None) mcp_server._stateful_session_owners.pop(session_id, None) @@ -2526,12 +2527,10 @@ async def test_stateful_mcp_get_stream_does_not_block_post(): session_id = "stream-session-1" owner_auth = UserAPIKeyAuth(api_key="owner-key", user_id="owner") - mcp_server._stateful_session_auth_contexts[session_id] = ( - mcp_server.MCPAuthenticatedUser(user_api_key_auth=owner_auth) - ) - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth + mcp_server._stateful_session_auth_contexts[session_id] = mcp_server.MCPAuthenticatedUser( + user_api_key_auth=owner_auth ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) stream_release = asyncio.Event() post_finished = asyncio.Event() @@ -2569,9 +2568,7 @@ async def test_stateful_mcp_get_stream_does_not_block_post(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=handle), patch.object( session_manager_stateful, "_server_instances", @@ -2615,10 +2612,7 @@ def test_jsonrpc_text_has_top_level_method_ignores_nested_method(): assert _jsonrpc_text_has_top_level_method(reordered) is True # response whose result nests a "method" key (and arrays of them) - response = ( - '{"jsonrpc":"2.0","id":1,"result":{"toolResult":{"method":"GET"},' - '"steps":[{"method":"x"}]}}' - ) + response = '{"jsonrpc":"2.0","id":1,"result":{"toolResult":{"method":"GET"},"steps":[{"method":"x"}]}}' assert _jsonrpc_text_has_top_level_method(response) is False # truncated response: result value never closes, no top-level method seen @@ -2643,12 +2637,10 @@ async def test_truncated_jsonrpc_response_with_nested_method_skips_lock(): session_id = "nested-method-response-session" owner_auth = UserAPIKeyAuth(api_key="owner-key", user_id="owner") - mcp_server._stateful_session_auth_contexts[session_id] = ( - mcp_server.MCPAuthenticatedUser(user_api_key_auth=owner_auth) - ) - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth + mcp_server._stateful_session_auth_contexts[session_id] = mcp_server.MCPAuthenticatedUser( + user_api_key_auth=owner_auth ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) gate = asyncio.Event() request_in_handle = asyncio.Event() @@ -2685,8 +2677,7 @@ async def test_truncated_jsonrpc_response_with_nested_method_skips_lock(): # parsed, with a nested "method" key in the first bytes to trip a flat # substring heuristic. response_body = ( - '{"jsonrpc":"2.0","id":99,"result":{"toolResult":' - '{"method":"GET","payload":"' + ("x" * 5000) + '"}}}' + '{"jsonrpc":"2.0","id":99,"result":{"toolResult":{"method":"GET","payload":"' + ("x" * 5000) + '"}}}' ).encode() try: @@ -2700,9 +2691,7 @@ async def test_truncated_jsonrpc_response_with_nested_method_skips_lock(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=handle), patch.object( session_manager_stateful, "_server_instances", @@ -2774,13 +2763,9 @@ async def test_mcp_routing_with_conflicting_alias_and_group_name(): mock_get_tools_spy = AsyncMock(return_value=[]) # Mock the function that checks DB for an access group named "custom_solutions" - mock_db_lookup = AsyncMock( - return_value=[specific_server.server_id, other_server.server_id] - ) + mock_db_lookup = AsyncMock(return_value=[specific_server.server_id, other_server.server_id]) - mock_get_allowed = AsyncMock( - return_value=[specific_server.server_id, other_server.server_id] - ) + mock_get_allowed = AsyncMock(return_value=[specific_server.server_id, other_server.server_id]) with ( patch( @@ -2805,14 +2790,12 @@ async def test_mcp_routing_with_conflicting_alias_and_group_name(): ) # Get the list of actual server objects that the orchestrator tried to contact - called_servers = [ - call.kwargs["server"] for call in mock_get_tools_spy.call_args_list - ] + called_servers = [call.kwargs["server"] for call in mock_get_tools_spy.call_args_list] assert len(called_servers) == 1, "Should have resolved to exactly one server." - assert ( - called_servers[0].server_id == specific_server.server_id - ), "Should have contacted the specific server alias, not the group." + assert called_servers[0].server_id == specific_server.server_id, ( + "Should have contacted the specific server alias, not the group." + ) @pytest.mark.asyncio @@ -2926,9 +2909,7 @@ async def test_oauth2_caller_headers_not_forwarded_for_migrated_server(): ) # Verify that _create_mcp_client was called - assert ( - mock_create_client.call_count == 1 - ), "Expected _create_mcp_client to be called once" + assert mock_create_client.call_count == 1, "Expected _create_mcp_client to be called once" # Verify the server passed to _create_mcp_client is the OAuth2 server assert captured_client_args["server"].server_id == oauth2_server.server_id @@ -2938,9 +2919,9 @@ async def test_oauth2_caller_headers_not_forwarded_for_migrated_server(): # oauth2 Authorization upstream. The v2 resolver injects the stored per-user token, # so a caller-supplied bearer cannot override another user's stored credential. extra_headers = captured_client_args["extra_headers"] - assert extra_headers is None or "Authorization" not in { - k.lower() for k in extra_headers - }, f"Caller Authorization must not be forwarded, got {extra_headers}" + assert extra_headers is None or "Authorization" not in {k.lower() for k in extra_headers}, ( + f"Caller Authorization must not be forwarded, got {extra_headers}" + ) @pytest.mark.asyncio @@ -3049,12 +3030,8 @@ async def test_list_tools_multiple_servers_prefixed_names(): # Mock manager mock_manager = MagicMock() - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["server1", "server2"] - ) - mock_manager.get_mcp_server_by_id = lambda server_id: ( - server1 if server_id == "server1" else server2 - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["server1", "server2"]) + mock_manager.get_mcp_server_by_id = lambda server_id: server1 if server_id == "server1" else server2 # Mock filter_server_ids_by_ip to return server_ids unchanged (no IP filtering) mock_manager.filter_server_ids_by_ip_with_info = lambda server_ids, client_ip: ( server_ids, @@ -3653,9 +3630,7 @@ async def test_list_tools_strips_prefix_when_matching_permissions(): tool1.inputSchema = {} tool2 = MagicMock() - tool2.name = ( - "GITMCP-search_litellm_documentation" # Prefixed, not in allowed list - ) + tool2.name = "GITMCP-search_litellm_documentation" # Prefixed, not in allowed list tool2.description = "Search docs" tool2.inputSchema = {} @@ -3876,17 +3851,13 @@ class TestMCPServerManagerReload: db_row = _make_db_mcp_server("server-1", timestamp) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[db_row] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[db_row]) with ( patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", return_value=mock_prisma, ), - patch.object( - manager, "build_mcp_server_from_table", AsyncMock() - ) as mock_build, + patch.object(manager, "build_mcp_server_from_table", AsyncMock()) as mock_build, ): await manager.reload_servers_from_database() @@ -3922,9 +3893,7 @@ class TestMCPServerManagerReload: ) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[db_row] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[db_row]) with ( patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", @@ -4049,9 +4018,7 @@ class TestMCPServerManagerReload: raise RuntimeError("blocked address") mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[healthy_row, bad_openapi_row] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[healthy_row, bad_openapi_row]) with ( patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", @@ -4147,10 +4114,7 @@ async def test_call_mcp_tool_logs_failure_via_post_call_failure_hook(): ) proxy_logging_mock.post_call_failure_hook.assert_awaited_once() - assert ( - proxy_logging_mock.post_call_failure_hook.await_args.kwargs.get("route") - == "/mcp/call_tool" - ) + assert proxy_logging_mock.post_call_failure_hook.await_args.kwargs.get("route") == "/mcp/call_tool" @pytest.mark.asyncio @@ -4232,9 +4196,7 @@ async def test_get_tools_from_mcp_servers_logs_list_tools_to_spendlogs_when_enab assert tools == [tool_1] dummy_logging_obj.async_success_handler.assert_awaited_once() - assert dummy_logging_obj.async_success_handler.await_args.kwargs["result"] == [ - tool_1.model_dump(mode="json") - ] + assert dummy_logging_obj.async_success_handler.await_args.kwargs["result"] == [tool_1.model_dump(mode="json")] assert function_setup_kwargs["metadata"]["tags"] == ["team-a"] spend_meta = dummy_logging_obj.model_call_details["metadata"]["spend_logs_metadata"] @@ -4580,9 +4542,7 @@ async def test_get_tools_from_mcp_servers_injects_stored_oauth2_token(): oauth2_server.extra_headers = None # Simulate the DB returning a valid credential for this user+server - prefetched_creds = { - SERVER_ID: {"access_token": STORED_TOKEN, "server_id": SERVER_ID} - } + prefetched_creds = {SERVER_ID: {"access_token": STORED_TOKEN, "server_id": SERVER_ID}} tool_1 = MagicMock() tool_1.name = "atlassian_test-search" @@ -4689,16 +4649,12 @@ class TestMergeGatewayInitializeInstructions: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "s1" - ] = "upstream" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["s1"] = "upstream" try: s = _make_instruction_server(instructions="yaml wins") assert self._merge([s]) == "yaml wins" finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "s1", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("s1", None) def test_upstream_cache_used_when_no_yaml(self): """Upstream cached instructions are used when no YAML override is set.""" @@ -4706,16 +4662,12 @@ class TestMergeGatewayInitializeInstructions: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "s1" - ] = "from upstream" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["s1"] = "from upstream" try: s = _make_instruction_server(instructions=None) assert self._merge([s]) == "from upstream" finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "s1", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("s1", None) def test_spec_path_servers_skipped(self): """OpenAPI (spec_path) servers do not contribute instructions.""" @@ -4729,12 +4681,8 @@ class TestMergeGatewayInitializeInstructions: def test_multiple_servers_merged_with_labels(self): """Multiple servers get label-prefixed and separator-joined.""" - s1 = _make_instruction_server( - server_id="a", name="a", alias="Alpha", instructions="instr A" - ) - s2 = _make_instruction_server( - server_id="b", name="b", alias="Beta", instructions="instr B" - ) + s1 = _make_instruction_server(server_id="a", name="a", alias="Alpha", instructions="instr A") + s2 = _make_instruction_server(server_id="b", name="b", alias="Beta", instructions="instr B") result = self._merge([s1, s2]) assert result is not None assert "[Alpha]" in result and "[Beta]" in result @@ -4754,25 +4702,17 @@ class TestMergeGatewayInitializeInstructions: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "c" - ] = "cached C" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["c"] = "cached C" try: - s_yaml = _make_instruction_server( - server_id="a", name="a", alias="A", instructions="yaml A" - ) - s_spec = _make_instruction_server( - server_id="b", name="b", alias="B", spec_path="/spec.json", url=None - ) + s_yaml = _make_instruction_server(server_id="a", name="a", alias="A", instructions="yaml A") + s_spec = _make_instruction_server(server_id="b", name="b", alias="B", spec_path="/spec.json", url=None) s_cached = _make_instruction_server(server_id="c", name="c", alias="C") result = self._merge([s_yaml, s_spec, s_cached]) assert "yaml A" in result assert "cached C" in result assert "[B]" not in result finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "c", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("c", None) class TestEnsureUpstreamInitializeInstructionsCached: @@ -4784,15 +4724,9 @@ class TestEnsureUpstreamInitializeInstructionsCached: global_mcp_server_manager, ) - server = _make_instruction_server( - server_id="yaml-only", instructions="from yaml" - ) - with patch.object( - global_mcp_server_manager, "_create_mcp_client", AsyncMock() - ) as mock_create: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + server = _make_instruction_server(server_id="yaml-only", instructions="from yaml") + with patch.object(global_mcp_server_manager, "_create_mcp_client", AsyncMock()) as mock_create: + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) mock_create.assert_not_awaited() @pytest.mark.asyncio @@ -4804,21 +4738,13 @@ class TestEnsureUpstreamInitializeInstructionsCached: ) server = _make_instruction_server(server_id="cached-only", instructions=None) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "cached-only" - ] = "warm" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["cached-only"] = "warm" try: - with patch.object( - global_mcp_server_manager, "_create_mcp_client", AsyncMock() - ) as mock_create: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + with patch.object(global_mcp_server_manager, "_create_mcp_client", AsyncMock()) as mock_create: + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) mock_create.assert_not_awaited() finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "cached-only", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("cached-only", None) @pytest.mark.asyncio async def test_skips_when_spec_path_set(self): @@ -4828,15 +4754,9 @@ class TestEnsureUpstreamInitializeInstructionsCached: global_mcp_server_manager, ) - server = _make_instruction_server( - server_id="openapi-spec", spec_path="/openapi.json", url=None - ) - with patch.object( - global_mcp_server_manager, "_create_mcp_client", AsyncMock() - ) as mock_create: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + server = _make_instruction_server(server_id="openapi-spec", spec_path="/openapi.json", url=None) + with patch.object(global_mcp_server_manager, "_create_mcp_client", AsyncMock()) as mock_create: + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) mock_create.assert_not_awaited() @pytest.mark.asyncio @@ -4858,22 +4778,14 @@ class TestEnsureUpstreamInitializeInstructionsCached: AsyncMock(return_value=fake_client), ): try: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) assert ( - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "cold-server" - ] + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["cold-server"] == "upstream says hi" ) finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "cold-server", None - ) - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "cold-server", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("cold-server", None) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("cold-server", None) @pytest.mark.asyncio async def test_cooldown_after_empty_upstream_response(self): @@ -4892,27 +4804,13 @@ class TestEnsureUpstreamInitializeInstructionsCached: create = AsyncMock(return_value=fake_client) with patch.object(global_mcp_server_manager, "_create_mcp_client", create): try: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - assert ( - create.await_count == 1 - ), "Second probe within cooldown must not reconnect to upstream" - assert ( - "empty-server" - not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id - ) - assert ( - "empty-server" - in global_mcp_server_manager._upstream_initialize_instructions_probed_at - ) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + assert create.await_count == 1, "Second probe within cooldown must not reconnect to upstream" + assert "empty-server" not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id + assert "empty-server" in global_mcp_server_manager._upstream_initialize_instructions_probed_at finally: - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "empty-server", None - ) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("empty-server", None) @pytest.mark.asyncio async def test_cooldown_after_upstream_failure(self): @@ -4925,35 +4823,19 @@ class TestEnsureUpstreamInitializeInstructionsCached: server = _make_instruction_server(server_id="boom-server", instructions=None) fake_client = MagicMock() - fake_client.run_with_session = AsyncMock( - side_effect=RuntimeError("upstream down") - ) + fake_client.run_with_session = AsyncMock(side_effect=RuntimeError("upstream down")) fake_client._last_initialize_instructions = None create = AsyncMock(return_value=fake_client) with patch.object(global_mcp_server_manager, "_create_mcp_client", create): try: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - assert ( - create.await_count == 1 - ), "Second probe within cooldown must not reconnect after failure" - assert ( - "boom-server" - not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id - ) - assert ( - "boom-server" - in global_mcp_server_manager._upstream_initialize_instructions_probed_at - ) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + assert create.await_count == 1, "Second probe within cooldown must not reconnect after failure" + assert "boom-server" not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id + assert "boom-server" in global_mcp_server_manager._upstream_initialize_instructions_probed_at finally: - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "boom-server", None - ) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("boom-server", None) @pytest.mark.asyncio async def test_reload_resets_probe_cooldown(self): @@ -4962,19 +4844,12 @@ class TestEnsureUpstreamInitializeInstructionsCached: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_probed_at[ - "reload-target" - ] = 1.0 + global_mcp_server_manager._upstream_initialize_instructions_probed_at["reload-target"] = 1.0 try: await global_mcp_server_manager.load_servers_from_config({}) - assert ( - "reload-target" - not in global_mcp_server_manager._upstream_initialize_instructions_probed_at - ) + assert "reload-target" not in global_mcp_server_manager._upstream_initialize_instructions_probed_at finally: - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "reload-target", None - ) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("reload-target", None) class TestGatewayCreateInitializationOptions: @@ -5040,9 +4915,7 @@ class TestGatewayCreateInitializationOptions: ): assert server.create_initialization_options().server_name == "grafana" - assert ( - server.create_initialization_options().server_name == "litellm-mcp-server" - ) + assert server.create_initialization_options().server_name == "litellm-mcp-server" @pytest.mark.asyncio async def test_sse_handler_scopes_server_name_from_single_server_path(self): @@ -5119,9 +4992,7 @@ class TestGatewayCreateInitializationOptions: await handle_sse_mcp(scope, AsyncMock(), AsyncMock()) assert captured["server_name"] == "grafana" - assert ( - server.create_initialization_options().server_name == "litellm-mcp-server" - ) + assert server.create_initialization_options().server_name == "litellm-mcp-server" def test_contextvar_set_injects_instructions(self): """When ContextVar has a value, it appears in InitializationOptions.""" @@ -5239,12 +5110,8 @@ async def test_list_tools_with_legacy_db_m2m_server_resolves_oauth2_flow(): ): mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["legacy-m2m-id"]) mock_manager.get_mcp_server_by_id = MagicMock(return_value=legacy_server) - mock_manager.filter_server_ids_by_ip_with_info = MagicMock( - return_value=(["legacy-m2m-id"], 0) - ) - mock_manager._get_tools_from_server = AsyncMock( - side_effect=capture_extra_headers - ) + mock_manager.filter_server_ids_by_ip_with_info = MagicMock(return_value=(["legacy-m2m-id"], 0)) + mock_manager._get_tools_from_server = AsyncMock(side_effect=capture_extra_headers) tools = await _get_tools_from_mcp_servers( user_api_key_auth=user_auth, @@ -5336,10 +5203,8 @@ async def test_call_tool_empty_extra_headers_returns_none(): pass # We only care about the captured headers # With P2 fix: extra_headers should be None (not {}) when all headers filtered - assert ( - captured_extra_headers is None - ), "P2 API consistency issue: expected None for empty extra_headers, got: " + str( - captured_extra_headers + assert captured_extra_headers is None, ( + "P2 API consistency issue: expected None for empty extra_headers, got: " + str(captured_extra_headers) ) @@ -5364,9 +5229,7 @@ async def test_probe_upstream_auth_returns_upstream_status(): "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", return_value=mock_client, ): - status, www_auth = await _probe_upstream_auth( - "http://upstream/mcp", "Bearer some-token" - ) + status, www_auth = await _probe_upstream_auth("http://upstream/mcp", "Bearer some-token") assert status == 401 assert www_auth == 'Bearer realm="test"' @@ -5393,9 +5256,7 @@ async def test_probe_upstream_auth_surfaces_httpx_status_error(): mock_response.status_code = 401 mock_response.headers = {"www-authenticate": 'Bearer realm="test"'} request = httpx.Request("POST", "http://upstream/mcp") - error = httpx.HTTPStatusError( - message="401 Unauthorized", request=request, response=mock_response - ) + error = httpx.HTTPStatusError(message="401 Unauthorized", request=request, response=mock_response) mock_client = MagicMock() mock_client.post = AsyncMock(side_effect=error) @@ -5404,9 +5265,7 @@ async def test_probe_upstream_auth_surfaces_httpx_status_error(): "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", return_value=mock_client, ): - status, www_auth = await _probe_upstream_auth( - "http://upstream/mcp", "Bearer some-token" - ) + status, www_auth = await _probe_upstream_auth("http://upstream/mcp", "Bearer some-token") assert status == 401 assert www_auth == 'Bearer realm="test"' @@ -5424,9 +5283,7 @@ async def test_probe_upstream_auth_fails_open_on_network_error(): "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", return_value=mock_client, ): - status, www_auth = await _probe_upstream_auth( - "http://upstream/mcp", "Bearer some-token" - ) + status, www_auth = await _probe_upstream_auth("http://upstream/mcp", "Bearer some-token") assert status == 200 assert www_auth is None @@ -6349,9 +6206,7 @@ class TestProxyExceptionToHttpException: from litellm.proxy._types import ProxyException http_exc = _proxy_exception_to_http_exception( - ProxyException( - message="Forbidden", type="auth_error", param="key", code=403 - ) + ProxyException(message="Forbidden", type="auth_error", param="key", code=403) ) assert http_exc.status_code == 403 @@ -6415,10 +6270,7 @@ class TestStreamableHttpAuthErrorMapping: assert exc_info.value.status_code == 401 assert exc_info.value.headers["WWW-Authenticate"] == "Bearer" # Must not have emitted a 500 body via the generic catch-all. - assert not any( - m.get("type") == "http.response.start" and m.get("status") == 500 - for m in sent - ) + assert not any(m.get("type") == "http.response.start" and m.get("status") == 500 for m in sent) @pytest.mark.asyncio async def test_sse_propagates_proxy_exception_as_401(self): @@ -6458,10 +6310,7 @@ class TestStreamableHttpAuthErrorMapping: assert exc_info.value.status_code == 401 assert exc_info.value.headers["WWW-Authenticate"] == "Bearer" - assert not any( - m.get("type") == "http.response.start" and m.get("status") == 500 - for m in sent - ) + assert not any(m.get("type") == "http.response.start" and m.get("status") == 500 for m in sent) class TestMCPMetaTraceCarrier: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index a7a0379c7ad..dc1a950f706 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -1490,6 +1490,118 @@ class TestMCPServerManager: assert emitted.headers["Authorization"] == "Bearer upstream-token" assert not kwargs["extra_headers"] or "authorization" not in {k.lower() for k in kwargs["extra_headers"]} + @staticmethod + def _emitted_authorization(mock_client_cls) -> str: + kwargs = mock_client_cls.call_args.kwargs + emitted = httpx.Request("GET", "https://example.com/mcp") + flow = kwargs["resolved_auth"].auth_flow(emitted) + next(flow) + flow.close() + return emitted.headers["Authorization"] + + @pytest.mark.asyncio + @pytest.mark.parametrize( + "per_server_header", + ["Bearer per-server-token", {"Authorization": "Bearer per-server-token"}], + ) + async def test_create_mcp_client_passthrough_prefers_per_server_token(self, per_server_header): + """A per-server x-mcp-{alias}-authorization value is the explicit one-token-one-server + binding, so it must win over the request-wide Authorization and reach the upstream + verbatim through the passthrough arm.""" + manager = MCPServerManager() + server = MCPServer( + server_id="tp-per-server", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + new_callable=AsyncMock, + ) as mock_resolve, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + ): + await manager._create_mcp_client( + server=server, + mcp_auth_header=per_server_header, + extra_headers={"Authorization": "Bearer global-token"}, + ) + mock_resolve.assert_not_awaited() + assert self._emitted_authorization(mock_client_cls) == "Bearer per-server-token" + kwargs = mock_client_cls.call_args.kwargs + assert not kwargs["extra_headers"] or "authorization" not in {k.lower() for k in kwargs["extra_headers"]} + + def test_consumes_caller_authorization_per_mode(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _consumes_caller_authorization, + ) + + def build(**kwargs) -> MCPServer: + return MCPServer( + server_id="s", + name="s", + url="https://example.com", + transport=MCPTransport.http, + **kwargs, + ) + + assert _consumes_caller_authorization(build(auth_type=MCPAuth.true_passthrough)) is True + assert _consumes_caller_authorization(build(auth_type=MCPAuth.oauth_delegate)) is True + assert ( + _consumes_caller_authorization( + build(auth_type=MCPAuth.none, extra_headers=["Authorization"], oauth_passthrough=True) + ) + is True + ) + assert _consumes_caller_authorization(build(auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True)) is True + assert _consumes_caller_authorization(build(auth_type=MCPAuth.api_key, authentication_token="x")) is False + assert ( + _consumes_caller_authorization( + build( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + oauth2_flow="client_credentials", + token_url="https://idp/token", + ) + ) + is False + ) + + def test_caller_authorization_fans_out_only_with_second_consumer(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _caller_authorization_fans_out, + ) + + delegate = MCPServer( + server_id="od", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + second = MCPServer( + server_id="tp", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + static_server = MCPServer( + server_id="static", + name="static", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="x", + ) + + assert _caller_authorization_fans_out(delegate, None) is False + assert _caller_authorization_fans_out(delegate, [delegate]) is False + assert _caller_authorization_fans_out(delegate, [delegate, static_server]) is False + assert _caller_authorization_fans_out(delegate, [delegate, second]) is True + @pytest.mark.asyncio async def test_get_prompts_from_server_success(self): """Ensure prompts are fetched and prefixed when requested.""" From 34db5f4813ab3449ef489a17b9d7b3da9d7c6635 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 16:26:47 +1000 Subject: [PATCH 005/399] feat(ui): add start time sort toggle to session logs sidebar --- .../LogDetailsDrawer.test.tsx | 105 ++++++++++++++++++ .../LogDetailsDrawer/LogDetailsDrawer.tsx | 39 ++++--- .../view_logs/LogDetailsDrawer/utils.test.ts | 30 +++++ .../view_logs/LogDetailsDrawer/utils.ts | 21 ++++ 4 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx new file mode 100644 index 00000000000..db0d168fcac --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -0,0 +1,105 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { LogDetailsDrawer } from "./LogDetailsDrawer"; +import { sessionSpendLogsCall } from "../../networking"; +import { LogEntry } from "../columns"; + +vi.mock("../../networking", () => ({ + sessionSpendLogsCall: vi.fn(), +})); + +vi.mock("@/app/(dashboard)/hooks/logDetails/useLogDetails", () => ({ + useLogDetails: () => ({ data: null, isLoading: false }), +})); + +vi.mock("./LogDetailContent", () => ({ + LogDetailContent: () => null, + GuardrailJumpLink: () => null, +})); + +vi.mock("./DrawerHeader", () => ({ + DrawerHeader: () => null, +})); + +const makeLog = (overrides: Partial): LogEntry => ({ + request_id: "req", + api_key: "", + team_id: "", + model: "", + model_id: "", + call_type: "acompletion", + spend: 0, + total_tokens: 0, + prompt_tokens: 0, + completion_tokens: 0, + startTime: "2026-07-08T10:00:00.000Z", + endTime: "2026-07-08T10:00:01.000Z", + cache_hit: "false", + messages: [], + response: {}, + ...overrides, +}); + +const sessionLogs = [ + makeLog({ + request_id: "llm-early", + model: "llm-early", + startTime: "2026-07-08T10:00:00.000Z", + endTime: "2026-07-08T10:00:02.000Z", + }), + makeLog({ + request_id: "mcp-early", + model: "tool-early", + call_type: "call_mcp_tool", + startTime: "2026-07-08T10:00:01.000Z", + endTime: "2026-07-08T10:00:01.500Z", + }), + makeLog({ + request_id: "llm-late", + model: "llm-late", + startTime: "2026-07-08T10:00:02.000Z", + endTime: "2026-07-08T10:00:04.000Z", + }), + makeLog({ + request_id: "mcp-late", + model: "tool-late", + call_type: "call_mcp_tool", + startTime: "2026-07-08T10:00:03.000Z", + endTime: "2026-07-08T10:00:03.500Z", + }), +]; + +const renderSessionDrawer = () => { + vi.mocked(sessionSpendLogsCall).mockResolvedValue({ data: sessionLogs, total: 4, total_pages: 1 }); + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + render( + + {}} logEntry={null} sessionId="session-1" accessToken="token" /> + , + ); +}; + +const sidebarEventNames = () => + screen.queryAllByText(/^(llm-early|llm-late|tool-early|tool-late)$/).map((el) => el.textContent); + +describe("LogDetailsDrawer session sidebar sorting", () => { + it("defaults to grouped order: LLM calls newest first, MCP calls grouped last", async () => { + renderSessionDrawer(); + await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); + expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"]); + }); + + it("switches to chronological order across LLM and MCP calls when Start time is selected", async () => { + renderSessionDrawer(); + await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); + + fireEvent.click(screen.getByText("Start time")); + + await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-early", "tool-early", "llm-late", "tool-late"])); + + fireEvent.click(screen.getByText("Grouped")); + + await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"])); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index bf3360a5371..36299139a13 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { Button, Drawer } from "antd"; +import { Button, Drawer, Segmented } from "antd"; import { CheckOutlined, CopyOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons"; import { Bot, Sparkles, Wrench } from "lucide-react"; import { LogEntry } from "../columns"; @@ -11,7 +11,7 @@ import { LogDetailContent, GuardrailJumpLink } from "./LogDetailContent"; import { sessionSpendLogsCall } from "../../networking"; import { useQuery } from "@tanstack/react-query"; import { getSpendString } from "@/utils/dataUtils"; -import { normalizeGuardrailEntries } from "./utils"; +import { normalizeGuardrailEntries, sortSessionLogs, SessionLogSortMode } from "./utils"; import { DRAWER_WIDTH } from "./constants"; import { useLogDetails } from "@/app/(dashboard)/hooks/logDetails/useLogDetails"; @@ -117,6 +117,7 @@ export function LogDetailsDrawer({ }: LogDetailsDrawerProps) { const isSessionMode = Boolean(sessionId); const [selectedSessionRequestId, setSelectedSessionRequestId] = useState(null); + const [sessionSortMode, setSessionSortMode] = useState("grouped"); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [copiedLeftPanelId, setCopiedLeftPanelId] = useState(false); @@ -152,26 +153,20 @@ export function LogDetailsDrawer({ // backend omits total, so the truncation note reflects what was fetched. const total: number = firstPage.total ?? rows.length; - const logs = rows - .map((row) => ({ - ...row, - request_duration_ms: row.request_duration_ms ?? Date.parse(row.endTime) - Date.parse(row.startTime), - })) - .sort((a, b) => { - const aIsMcp = MCP_CALL_TYPES.includes(a.call_type) ? 1 : 0; - const bIsMcp = MCP_CALL_TYPES.includes(b.call_type) ? 1 : 0; - if (aIsMcp !== bIsMcp) return aIsMcp - bIsMcp; - // Newest first, matching the all-sessions logs overview. MCP calls - // stay grouped last (above), newest-first within that group too. - return new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); - }); + const logs = rows.map((row) => ({ + ...row, + request_duration_ms: row.request_duration_ms ?? Date.parse(row.endTime) - Date.parse(row.startTime), + })); return { logs, total }; }, enabled: Boolean(open && isSessionMode && sessionId && accessToken), }); - const sessionLogs: LogEntry[] = sessionData?.logs ?? []; + const sessionLogs: LogEntry[] = useMemo( + () => sortSessionLogs(sessionData?.logs ?? [], sessionSortMode), + [sessionData, sessionSortMode], + ); // total reported by the backend; when the page cap truncates the fetch this // exceeds sessionLogs.length, which drives the "showing most recent" note. const sessionTotalCount = sessionData?.total ?? sessionLogs.length; @@ -391,6 +386,18 @@ export function LogDetailsDrawer({ Showing most recent {logsForList.length} of {sessionTotalCount} )} + {isSessionMode && ( + setSessionSortMode(value as SessionLogSortMode)} + /> + )}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts new file mode 100644 index 00000000000..cbe12f5c101 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import { sortSessionLogs } from "./utils"; + +const llm = (id: string, startTime: string) => ({ request_id: id, call_type: "acompletion", startTime }); +const mcp = (id: string, startTime: string) => ({ request_id: id, call_type: "call_mcp_tool", startTime }); + +const ids = (rows: { request_id: string }[]) => rows.map((row) => row.request_id); + +describe("sortSessionLogs", () => { + const rows = [ + mcp("mcp-early", "2026-07-08T10:00:01.000Z"), + llm("llm-late", "2026-07-08T10:00:02.000Z"), + mcp("mcp-late", "2026-07-08T10:00:03.000Z"), + llm("llm-early", "2026-07-08T10:00:00.000Z"), + ]; + + it("grouped mode keeps MCP calls last, newest first within each group", () => { + expect(ids(sortSessionLogs(rows, "grouped"))).toEqual(["llm-late", "llm-early", "mcp-late", "mcp-early"]); + }); + + it("chronological mode interleaves all calls by start time, oldest first", () => { + expect(ids(sortSessionLogs(rows, "chronological"))).toEqual(["llm-early", "mcp-early", "llm-late", "mcp-late"]); + }); + + it("does not mutate the input array", () => { + const input = [...rows]; + sortSessionLogs(input, "chronological"); + expect(ids(input)).toEqual(ids(rows)); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts index 61301cf5b54..5a1a0e81f96 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts @@ -3,6 +3,27 @@ * These functions handle data formatting, validation, and guardrail calculations. */ +import { MCP_CALL_TYPES } from "../constants"; + +export type SessionLogSortMode = "grouped" | "chronological"; + +export function sortSessionLogs( + rows: T[], + mode: SessionLogSortMode, +): T[] { + if (mode === "chronological") { + return [...rows].sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); + } + return [...rows].sort((a, b) => { + const aIsMcp = MCP_CALL_TYPES.includes(a.call_type) ? 1 : 0; + const bIsMcp = MCP_CALL_TYPES.includes(b.call_type) ? 1 : 0; + if (aIsMcp !== bIsMcp) return aIsMcp - bIsMcp; + // Newest first, matching the all-sessions logs overview. MCP calls + // stay grouped last (above), newest-first within that group too. + return new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); + }); +} + /** * Formats data for display. If input is a string, attempts to parse as JSON. * @param input - Data to format (string or object) From df2d44bab1e2c2ffd3acf68bbc0abf6ab1160f85 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 16:54:39 +1000 Subject: [PATCH 006/399] feat(ui): sort session sidebar by duration or start time --- .../LogDetailsDrawer.test.tsx | 12 +++---- .../LogDetailsDrawer/LogDetailsDrawer.tsx | 30 ++++++++-------- .../view_logs/LogDetailsDrawer/utils.test.ts | 36 +++++++++++++------ .../view_logs/LogDetailsDrawer/utils.ts | 23 +++++------- 4 files changed, 55 insertions(+), 46 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx index db0d168fcac..1d23fecb5da 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -53,13 +53,13 @@ const sessionLogs = [ model: "tool-early", call_type: "call_mcp_tool", startTime: "2026-07-08T10:00:01.000Z", - endTime: "2026-07-08T10:00:01.500Z", + endTime: "2026-07-08T10:00:06.000Z", }), makeLog({ request_id: "llm-late", model: "llm-late", startTime: "2026-07-08T10:00:02.000Z", - endTime: "2026-07-08T10:00:04.000Z", + endTime: "2026-07-08T10:00:05.000Z", }), makeLog({ request_id: "mcp-late", @@ -84,10 +84,10 @@ const sidebarEventNames = () => screen.queryAllByText(/^(llm-early|llm-late|tool-early|tool-late)$/).map((el) => el.textContent); describe("LogDetailsDrawer session sidebar sorting", () => { - it("defaults to grouped order: LLM calls newest first, MCP calls grouped last", async () => { + it("defaults to duration order, longest call first across LLM and MCP calls", async () => { renderSessionDrawer(); await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); - expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"]); + expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"]); }); it("switches to chronological order across LLM and MCP calls when Start time is selected", async () => { @@ -98,8 +98,8 @@ describe("LogDetailsDrawer session sidebar sorting", () => { await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-early", "tool-early", "llm-late", "tool-late"])); - fireEvent.click(screen.getByText("Grouped")); + fireEvent.click(screen.getByText("Duration")); - await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"])); + await waitFor(() => expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"])); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index 36299139a13..79592216942 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -117,7 +117,7 @@ export function LogDetailsDrawer({ }: LogDetailsDrawerProps) { const isSessionMode = Boolean(sessionId); const [selectedSessionRequestId, setSelectedSessionRequestId] = useState(null); - const [sessionSortMode, setSessionSortMode] = useState("grouped"); + const [sessionSortMode, setSessionSortMode] = useState("duration"); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [copiedLeftPanelId, setCopiedLeftPanelId] = useState(false); @@ -173,9 +173,9 @@ export function LogDetailsDrawer({ const sessionTruncated = sessionTotalCount > sessionLogs.length; // Default selection for a freshly opened session: the most recent log (latest - // startTime). The list is sorted newest-first, but MCP calls are grouped last, - // so the latest log by time is not necessarily sessionLogs[0]; compute it - // explicitly. A clicked/remembered log still wins over this default. + // startTime). The list is ordered by the selected sort mode, so the latest + // log by time is not necessarily sessionLogs[0]; compute it explicitly. + // A clicked/remembered log still wins over this default. const mostRecentLog = useMemo( () => sessionLogs.reduce( @@ -387,16 +387,18 @@ export function LogDetailsDrawer({
)} {isSessionMode && ( - setSessionSortMode(value as SessionLogSortMode)} - /> +
+ Sort by + setSessionSortMode(value as SessionLogSortMode)} + /> +
)} diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts index cbe12f5c101..f59a20529d0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts @@ -1,30 +1,44 @@ import { describe, expect, it } from "vitest"; import { sortSessionLogs } from "./utils"; -const llm = (id: string, startTime: string) => ({ request_id: id, call_type: "acompletion", startTime }); -const mcp = (id: string, startTime: string) => ({ request_id: id, call_type: "call_mcp_tool", startTime }); +const log = (id: string, startTime: string, endTime: string, request_duration_ms?: number) => ({ + request_id: id, + startTime, + endTime, + request_duration_ms, +}); const ids = (rows: { request_id: string }[]) => rows.map((row) => row.request_id); describe("sortSessionLogs", () => { const rows = [ - mcp("mcp-early", "2026-07-08T10:00:01.000Z"), - llm("llm-late", "2026-07-08T10:00:02.000Z"), - mcp("mcp-late", "2026-07-08T10:00:03.000Z"), - llm("llm-early", "2026-07-08T10:00:00.000Z"), + log("mid-duration", "2026-07-08T10:00:01.000Z", "2026-07-08T10:00:01.500Z", 2000), + log("longest", "2026-07-08T10:00:02.000Z", "2026-07-08T10:00:02.500Z", 5000), + log("shortest", "2026-07-08T10:00:03.000Z", "2026-07-08T10:00:03.500Z", 300), + log("earliest-no-duration-field", "2026-07-08T10:00:00.000Z", "2026-07-08T10:00:04.000Z"), ]; - it("grouped mode keeps MCP calls last, newest first within each group", () => { - expect(ids(sortSessionLogs(rows, "grouped"))).toEqual(["llm-late", "llm-early", "mcp-late", "mcp-early"]); + it("duration mode sorts longest call first, deriving duration from timestamps when the field is missing", () => { + expect(ids(sortSessionLogs(rows, "duration"))).toEqual([ + "longest", + "earliest-no-duration-field", + "mid-duration", + "shortest", + ]); }); - it("chronological mode interleaves all calls by start time, oldest first", () => { - expect(ids(sortSessionLogs(rows, "chronological"))).toEqual(["llm-early", "mcp-early", "llm-late", "mcp-late"]); + it("start_time mode sorts calls in the order they started", () => { + expect(ids(sortSessionLogs(rows, "start_time"))).toEqual([ + "earliest-no-duration-field", + "mid-duration", + "longest", + "shortest", + ]); }); it("does not mutate the input array", () => { const input = [...rows]; - sortSessionLogs(input, "chronological"); + sortSessionLogs(input, "duration"); expect(ids(input)).toEqual(ids(rows)); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts index 5a1a0e81f96..d313d07361c 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts @@ -3,25 +3,18 @@ * These functions handle data formatting, validation, and guardrail calculations. */ -import { MCP_CALL_TYPES } from "../constants"; +export type SessionLogSortMode = "duration" | "start_time"; -export type SessionLogSortMode = "grouped" | "chronological"; +type SortableSessionLog = { startTime: string; endTime: string; request_duration_ms?: number }; -export function sortSessionLogs( - rows: T[], - mode: SessionLogSortMode, -): T[] { - if (mode === "chronological") { +const durationMs = (row: SortableSessionLog): number => + row.request_duration_ms ?? Date.parse(row.endTime) - Date.parse(row.startTime); + +export function sortSessionLogs(rows: T[], mode: SessionLogSortMode): T[] { + if (mode === "start_time") { return [...rows].sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); } - return [...rows].sort((a, b) => { - const aIsMcp = MCP_CALL_TYPES.includes(a.call_type) ? 1 : 0; - const bIsMcp = MCP_CALL_TYPES.includes(b.call_type) ? 1 : 0; - if (aIsMcp !== bIsMcp) return aIsMcp - bIsMcp; - // Newest first, matching the all-sessions logs overview. MCP calls - // stay grouped last (above), newest-first within that group too. - return new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); - }); + return [...rows].sort((a, b) => durationMs(b) - durationMs(a)); } /** From 5d89be551bbed49c493f2a1cea0bddf4ea8e468b Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 17:06:38 +1000 Subject: [PATCH 007/399] fix(ui): fit session sort toggle inside sidebar column --- .../LogDetailsDrawer/LogDetailsDrawer.tsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index 79592216942..92cf90fad63 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -387,18 +387,17 @@ export function LogDetailsDrawer({ )} {isSessionMode && ( -
- Sort by - setSessionSortMode(value as SessionLogSortMode)} - /> -
+ setSessionSortMode(value as SessionLogSortMode)} + /> )} From 6d2090a21b19d6277d44367983d9d70ee10e8a0c Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 17:17:44 +1000 Subject: [PATCH 008/399] fix(ui): reset session sort mode when drawer closes --- .../LogDetailsDrawer.test.tsx | 21 ++++++++++++++++--- .../LogDetailsDrawer/LogDetailsDrawer.tsx | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx index 1d23fecb5da..5a49cccec70 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -73,11 +73,13 @@ const sessionLogs = [ const renderSessionDrawer = () => { vi.mocked(sessionSpendLogsCall).mockResolvedValue({ data: sessionLogs, total: 4, total_pages: 1 }); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - render( + const drawer = (open: boolean) => ( - {}} logEntry={null} sessionId="session-1" accessToken="token" /> - , + {}} logEntry={null} sessionId="session-1" accessToken="token" /> + ); + const { rerender } = render(drawer(true)); + return { rerender, drawer }; }; const sidebarEventNames = () => @@ -102,4 +104,17 @@ describe("LogDetailsDrawer session sidebar sorting", () => { await waitFor(() => expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"])); }); + + it("resets the sort mode back to duration when the drawer is closed and reopened", async () => { + const { rerender, drawer } = renderSessionDrawer(); + await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); + + fireEvent.click(screen.getByText("Start time")); + await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-early", "tool-early", "llm-late", "tool-late"])); + + rerender(drawer(false)); + rerender(drawer(true)); + + await waitFor(() => expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"])); + }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index 92cf90fad63..cc087a611b0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -217,6 +217,7 @@ export function LogDetailsDrawer({ setIsSidebarCollapsed(false); } else { if (isSessionMode) setSelectedSessionRequestId(null); + setSessionSortMode("duration"); setCopiedLeftPanelId(false); } }, [open, isSessionMode]); From bfff5e8d868312fcec9fe7fd9aaa3df14aa31ea3 Mon Sep 17 00:00:00 2001 From: Yassin Kortam Date: Wed, 8 Jul 2026 19:02:48 +0300 Subject: [PATCH 009/399] fix(mcp): log MCP tool calls returning isError=true as failures (#32238) An MCP tool call that completes with CallToolResult.isError=true correctly returns HTTP 200 per the MCP spec, but the shared post-call logging helper always fired async_success_handler, so the standard logging payload carried status=success and OTel (whose _parse_error only marks ERROR on status=failure) showed green spans for failed tools. The helper now checks the result after async_post_mcp_tool_call_hook runs (guardrails may flip isError there) and routes error results to the failure path: success gates are consumed so the @client wrapper cannot enqueue a success log, failure_handler and async_failure_handler fire with a new MCPToolResultError carrying the tool's first text content, and post_call_failure_hook records the failure the same way raised exceptions already do. Raised exceptions never reach the helper, so no double failure logging. HTTP wire behavior is unchanged Resolves LIT-4081 --- .../_experimental/mcp_server/exceptions.py | 15 + .../mcp_server/rest_endpoints.py | 36 ++- .../proxy/_experimental/mcp_server/server.py | 72 ++++- .../proxy/_experimental/mcp_server/utils.py | 19 ++ .../mcp_server/test_mcp_server.py | 304 ++++++++++++++++++ .../mcp_server/test_mcp_tool_search.py | 2 +- .../mcp_server/test_rest_endpoints.py | 6 +- 7 files changed, 440 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/exceptions.py b/litellm/proxy/_experimental/mcp_server/exceptions.py index b3f7ca9bbe2..3e3e549008d 100644 --- a/litellm/proxy/_experimental/mcp_server/exceptions.py +++ b/litellm/proxy/_experimental/mcp_server/exceptions.py @@ -73,3 +73,18 @@ class MCPUpstreamAuthError(Exception): detail=detail, headers={"www-authenticate": challenge} if challenge else None, ) + + +class MCPToolResultError(Exception): + """An MCP tool call completed with ``isError=True`` in its result. + + Never raised on the wire path: streamable HTTP MCP correctly returns tool + failures as HTTP 200 with ``result.isError: true`` per the MCP spec. This + exception only drives the standard failure logging (``status="failure"`` + payload, OTel ERROR span) for such results. + + Lives here rather than ``utils.py`` deliberately: tests reload ``utils`` + to re-read its env-derived constants, and a reload would fork this class + into two identities, breaking ``isinstance`` checks against instances + created before the reload. + """ diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index d482e537c5d..b917530dd52 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -8,6 +8,7 @@ from typing import ( Dict, List, Literal, + Mapping, Optional, Set, Tuple, @@ -78,7 +79,7 @@ if MCP_AVAILABLE: MCPInfo, MCPServer, _apply_toolset_scope, - _fire_mcp_success_logging, + _fire_mcp_tool_call_logging, _tool_name_matches, execute_mcp_tool, filter_tools_by_allowed_tools, @@ -86,23 +87,32 @@ if MCP_AVAILABLE: ######################################################## ############ MCP Server REST API Routes ################# - async def _safe_fire_mcp_success_logging( + async def _safe_fire_mcp_tool_call_logging( logging_obj: Optional[Any], result: Any, start_time: datetime, end_time: datetime, + user_api_key_auth: Optional[UserAPIKeyAuth] = None, + request_data: Optional[Mapping[str, object]] = None, ) -> None: if logging_obj is None: return logging_results = await asyncio.gather( - _fire_mcp_success_logging(logging_obj, result, start_time, end_time), + _fire_mcp_tool_call_logging( + logging_obj, + result, + start_time, + end_time, + user_api_key_auth=user_api_key_auth, + request_data=request_data, + ), return_exceptions=True, ) logging_error = logging_results[0] if isinstance(logging_error, asyncio.CancelledError): raise logging_error if isinstance(logging_error, BaseException): - verbose_logger.warning("MCP tool success logging failed (continuing): %s", logging_error) + verbose_logger.warning("MCP tool call logging failed (continuing): %s", logging_error) def _get_server_auth_header( server, @@ -872,7 +882,14 @@ if MCP_AVAILABLE: raw_headers=virtual_raw_headers, litellm_logging_obj=virtual_logging_obj, ) - await _safe_fire_mcp_success_logging(virtual_logging_obj, result, _tool_start_time, datetime.now()) + await _safe_fire_mcp_tool_call_logging( + virtual_logging_obj, + result, + _tool_start_time, + datetime.now(), + user_api_key_auth=user_api_key_dict, + request_data=data, + ) return result # Validate required parameters early @@ -955,7 +972,14 @@ if MCP_AVAILABLE: litellm_logging_obj=data.get("litellm_logging_obj"), requested_server_id=canonical_server_id, ) - await _safe_fire_mcp_success_logging(logging_obj, result, _tool_start_time, datetime.now()) + await _safe_fire_mcp_tool_call_logging( + logging_obj, + result, + _tool_start_time, + datetime.now(), + user_api_key_auth=user_api_key_dict, + request_data=data, + ) return result except MCPMissingUserEnvVarsError as e: verbose_logger.info( diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index fc847182a60..c03e49a1628 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -20,6 +20,7 @@ from typing import ( Callable, Dict, List, + Mapping, Optional, Set, Tuple, @@ -47,7 +48,10 @@ from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( get_request_base_url, ) -from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError +from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPToolResultError, + MCPUpstreamAuthError, +) from litellm.proxy._experimental.mcp_server.mcp_context import ( _mcp_active_toolset_id, _mcp_gateway_initialize_instructions, @@ -60,6 +64,7 @@ from litellm.proxy._experimental.mcp_server.utils import ( LITELLM_MCP_SERVER_VERSION, MCPMissingUserEnvVarsError, add_server_prefix_to_name, + extract_mcp_tool_result_error_message, get_server_prefix, iter_known_server_prefixes, ) @@ -2743,12 +2748,40 @@ if MCP_AVAILABLE: return response - async def _fire_mcp_success_logging( + _MCP_CREDENTIAL_REQUEST_FIELDS = frozenset( + { + "raw_headers", + "mcp_auth_header", + "mcp_server_auth_headers", + "oauth2_headers", + "user_api_key_auth", + } + ) + + async def _fire_mcp_tool_call_logging( logging_obj: LiteLLMLoggingObj, result: Any, start_time: datetime, end_time: datetime, + user_api_key_auth: Optional[UserAPIKeyAuth] = None, + request_data: Optional[Mapping[str, object]] = None, ) -> None: + """Fire post-call logging for an executed MCP tool call. + + A result with ``isError=True`` is logged as a failure (``status="failure"`` + payload, so OTel marks the span ERROR) while the HTTP wire behavior stays + 200 + ``isError: true`` per the MCP spec. The error check runs after + ``async_post_mcp_tool_call_hook`` because guardrails may flip the result + to ``isError=True`` in that hook. Raised exceptions never reach here (the + ``@client`` wrapper and ``call_mcp_tool``'s except path log those), so + this cannot double-log a failure. + + ``request_data`` may carry credential-bearing fields (the REST path puts + ``raw_headers``, ``mcp_auth_header``, ``mcp_server_auth_headers``, and + ``oauth2_headers`` at the top level of its data dict), so those are + stripped before the dict is handed to ``post_call_failure_hook`` + callbacks. + """ logging_obj.post_call(original_response=result) await logging_obj.async_post_mcp_tool_call_hook( kwargs=logging_obj.model_call_details, @@ -2757,7 +2790,31 @@ if MCP_AVAILABLE: end_time=end_time, ) logging_obj.call_type = CallTypes.call_mcp_tool.value - await logging_obj.async_success_handler(result=result, start_time=start_time, end_time=end_time) + error_message = extract_mcp_tool_result_error_message(result) + if error_message is None: + await logging_obj.async_success_handler(result=result, start_time=start_time, end_time=end_time) + return + + logging_obj.has_run_logging(event_type="sync_success") + logging_obj.has_run_logging(event_type="async_success") + tool_error = MCPToolResultError(error_message) + logging_obj.failure_handler(tool_error, "", start_time, end_time) + await logging_obj.async_failure_handler(tool_error, "", start_time, end_time) + + if user_api_key_auth is None: + return + from litellm.proxy.proxy_server import proxy_logging_obj + + if proxy_logging_obj: + sanitized_request_data = { + key: value for key, value in (request_data or {}).items() if key not in _MCP_CREDENTIAL_REQUEST_FIELDS + } + await proxy_logging_obj.post_call_failure_hook( + request_data=sanitized_request_data, + original_exception=tool_error, + user_api_key_dict=user_api_key_auth, + route="/mcp/call_tool", + ) @client async def call_mcp_tool( @@ -2833,7 +2890,14 @@ if MCP_AVAILABLE: raise if litellm_logging_obj: - await _fire_mcp_success_logging(litellm_logging_obj, response, start_time, datetime.now()) + await _fire_mcp_tool_call_logging( + logging_obj=litellm_logging_obj, + result=response, + start_time=start_time, + end_time=datetime.now(), + user_api_key_auth=user_api_key_auth, + request_data=kwargs, + ) return response async def mcp_get_prompt( diff --git a/litellm/proxy/_experimental/mcp_server/utils.py b/litellm/proxy/_experimental/mcp_server/utils.py index c9c60030dbc..80a469b8c1a 100644 --- a/litellm/proxy/_experimental/mcp_server/utils.py +++ b/litellm/proxy/_experimental/mcp_server/utils.py @@ -415,6 +415,25 @@ def validate_mcp_server_name(server_name: str, raise_http_exception: bool = Fals raise Exception(error_message) +def extract_mcp_tool_result_error_message(result: object) -> Optional[str]: + """The first text content of an ``isError=True`` tool result, or ``None`` + when the result is not an error. + + Accepts both ``mcp.types.CallToolResult`` objects and their dict + equivalents, duck-typed so the ``mcp`` package is not required. + """ + is_error: object = result.get("isError") if isinstance(result, Mapping) else getattr(result, "isError", None) + if is_error is not True: + return None + content: object = result.get("content") if isinstance(result, Mapping) else getattr(result, "content", None) + if isinstance(content, (list, tuple)): + for item in content: + text: object = item.get("text") if isinstance(item, Mapping) else getattr(item, "text", None) + if isinstance(text, str) and text: + return text + return "MCP tool call returned isError=true" + + TOOL_DISPLAY_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_-]+$") diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index b24457deabd..83c19dfd7ca 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -8,8 +8,10 @@ from fastapi import HTTPException from mcp import ReadResourceResult, Resource from mcp.types import ( BlobResourceContents, + CallToolResult, Prompt, ResourceTemplate, + TextContent, TextResourceContents, ) @@ -6598,6 +6600,308 @@ async def test_get_active_submitted_mcp_server_ids_for_user_empty_user_id_skips_ prisma_client.db.litellm_mcpservertable.find_many.assert_not_awaited() +# --------------------------------------------------------------------------- # +# MCP tool-call isError failure logging +# --------------------------------------------------------------------------- # + + +def _call_tool_result(is_error: bool, text: str) -> CallToolResult: + return CallToolResult(content=[TextContent(type="text", text=text)], isError=is_error) + + +def _mock_mcp_logging_obj() -> MagicMock: + logging_obj = MagicMock() + logging_obj.model_call_details = {} + logging_obj.async_post_mcp_tool_call_hook = AsyncMock() + logging_obj.async_success_handler = AsyncMock() + logging_obj.async_failure_handler = AsyncMock() + return logging_obj + + +def test_extract_mcp_tool_result_error_message(): + from litellm.proxy._experimental.mcp_server.utils import ( + extract_mcp_tool_result_error_message, + ) + + assert extract_mcp_tool_result_error_message(_call_tool_result(True, "boom")) == "boom" + assert extract_mcp_tool_result_error_message(_call_tool_result(False, "ok")) is None + assert ( + extract_mcp_tool_result_error_message(CallToolResult(content=[], isError=True)) + == "MCP tool call returned isError=true" + ) + assert ( + extract_mcp_tool_result_error_message({"isError": True, "content": [{"type": "text", "text": "denied"}]}) + == "denied" + ) + assert extract_mcp_tool_result_error_message({"isError": False, "content": []}) is None + assert extract_mcp_tool_result_error_message({}) is None + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_logs_failure(): + """Regression test: a CallToolResult with isError=True must go + down the failure logging path (async_failure_handler + post_call_failure_hook), + never async_success_handler.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + from litellm.proxy._experimental.mcp_server.exceptions import MCPToolResultError + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + user_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user") + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "upstream exploded"), + start_time=datetime.now(), + end_time=datetime.now(), + user_api_key_auth=user_auth, + request_data={"litellm_call_id": "cid"}, + ) + + logging_obj.async_success_handler.assert_not_awaited() + logging_obj.failure_handler.assert_called_once() + logging_obj.async_failure_handler.assert_awaited_once() + tool_error = logging_obj.async_failure_handler.await_args.args[0] + assert isinstance(tool_error, MCPToolResultError) + assert str(tool_error) == "upstream exploded" + logging_obj.has_run_logging.assert_any_call(event_type="sync_success") + logging_obj.has_run_logging.assert_any_call(event_type="async_success") + proxy_logging_mock.post_call_failure_hook.assert_awaited_once() + hook_kwargs = proxy_logging_mock.post_call_failure_hook.await_args.kwargs + assert hook_kwargs["route"] == "/mcp/call_tool" + assert hook_kwargs["original_exception"] is tool_error + assert hook_kwargs["user_api_key_dict"] is user_auth + logging_obj.async_post_mcp_tool_call_hook.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_success_path_unchanged(): + """isError=False must keep today's behavior: success handler fires, no + failure logging, no post_call_failure_hook.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + result = _call_tool_result(False, "all good") + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=result, + start_time=datetime.now(), + end_time=datetime.now(), + user_api_key_auth=UserAPIKeyAuth(api_key="test-key", user_id="test-user"), + request_data={}, + ) + + logging_obj.async_success_handler.assert_awaited_once() + assert logging_obj.async_success_handler.await_args.kwargs["result"] is result + logging_obj.async_failure_handler.assert_not_awaited() + logging_obj.failure_handler.assert_not_called() + proxy_logging_mock.post_call_failure_hook.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_without_auth_skips_failure_hook(): + """Without a UserAPIKeyAuth the failure handlers still fire but the proxy + post_call_failure_hook (which requires one) is skipped.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result={"isError": True, "content": [{"type": "text", "text": "denied"}]}, + start_time=datetime.now(), + end_time=datetime.now(), + ) + + logging_obj.async_success_handler.assert_not_awaited() + logging_obj.async_failure_handler.assert_awaited_once() + assert str(logging_obj.async_failure_handler.await_args.args[0]) == "denied" + proxy_logging_mock.post_call_failure_hook.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_strips_credentials_from_failure_hook(): + """Credential-bearing request_data fields (raw request headers, upstream MCP + auth headers, OAuth tokens) must never reach post_call_failure_hook + callbacks; non-credential fields must survive untouched.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + user_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user") + request_data = { + "name": "explode", + "litellm_call_id": "cid", + "raw_headers": {"authorization": "Bearer sk-caller-secret"}, + "mcp_auth_header": "upstream-secret", + "mcp_server_auth_headers": {"srv": {"authorization": "Bearer srv-secret"}}, + "oauth2_headers": {"authorization": "Bearer oauth-secret"}, + "user_api_key_auth": user_auth, + } + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "boom"), + start_time=datetime.now(), + end_time=datetime.now(), + user_api_key_auth=user_auth, + request_data=request_data, + ) + + proxy_logging_mock.post_call_failure_hook.assert_awaited_once() + hook_request_data = proxy_logging_mock.post_call_failure_hook.await_args.kwargs["request_data"] + assert hook_request_data == {"name": "explode", "litellm_call_id": "cid"} + assert "secret" not in str(hook_request_data) + + +def _real_mcp_logging_obj(call_id: str): + from litellm.litellm_core_utils.litellm_logging import Logging + + start_time = datetime.now() + logging_obj = Logging( + model="MCP: weather/get_forecast", + messages=[{"role": "user", "content": "tool call"}], + stream=False, + call_type="call_mcp_tool", + start_time=start_time, + litellm_call_id=call_id, + function_id="test-fn", + ) + logging_obj.update_environment_variables( + model="MCP: weather/get_forecast", + user="", + optional_params={}, + litellm_params={"api_base": ""}, + ) + logging_obj.model_call_details["mcp_tool_call_metadata"] = { + "name": "get_forecast", + "arguments": {"city": "Paris"}, + "mcp_server_name": "weather", + } + return logging_obj, start_time + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_builds_failure_payload(monkeypatch): + """The standard logging payload for an isError=True result must carry + status='failure' with the tool's error text, so OTel (whose _parse_error + keys off status) marks the MCP span ERROR.""" + import litellm + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", []) + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", []) + + logging_obj, start_time = _real_mcp_logging_obj("test-mcp-iserror-payload") + + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "upstream exploded"), + start_time=start_time, + end_time=datetime.now(), + ) + + payload = logging_obj.model_call_details["standard_logging_object"] + assert payload["status"] == "failure" + assert payload["error_str"] == "upstream exploded" + assert payload["error_information"]["error_class"] == "MCPToolResultError" + assert payload["metadata"]["mcp_tool_call_metadata"]["name"] == "get_forecast" + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_success_builds_success_payload(monkeypatch): + """isError=False still produces a status='success' payload.""" + import litellm + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", []) + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", []) + + logging_obj, start_time = _real_mcp_logging_obj("test-mcp-success-payload") + + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(False, "all good"), + start_time=start_time, + end_time=datetime.now(), + ) + + payload = logging_obj.model_call_details["standard_logging_object"] + assert payload["status"] == "success" + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_emits_otel_error_span(monkeypatch): + """End-to-end regression for the OTel symptom: an isError=True tool + result must reach OTel as an MCP span with StatusCode.ERROR and the tool's + error message, while isError=False stays non-error.""" + pytest.importorskip("opentelemetry") + from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, + ) + from opentelemetry.trace.status import StatusCode + + import litellm + from litellm.integrations.otel import OpenTelemetryV2Config + from litellm.integrations.otel.logger import OpenTelemetryV2 + from litellm.integrations.otel.plumbing import providers + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + cfg = OpenTelemetryV2Config(exporter="in_memory", legacy_compat=False) + exporter = InMemorySpanExporter() + tracer_provider = providers.build_tracer_provider(cfg, exporter=exporter) + otel_logger = OpenTelemetryV2(config=cfg, tracer_provider=tracer_provider) + + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", [otel_logger]) + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", [otel_logger]) + + logging_obj, start_time = _real_mcp_logging_obj("test-mcp-iserror-otel") + + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "upstream exploded"), + start_time=start_time, + end_time=datetime.now(), + ) + + (span,) = exporter.get_finished_spans() + assert span.name == "tools/call get_forecast" + assert span.status.status_code is StatusCode.ERROR + assert span.attributes["error.type"] == "MCPToolResultError" + assert "upstream exploded" in (span.status.description or "") + + @pytest.mark.asyncio async def test_call_tool_with_legacy_db_m2m_server_resolves_oauth2_flow(): """ diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py index 5c2a04456b0..b8f0b205831 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py @@ -433,7 +433,7 @@ class TestCallToolRestApiVirtualTools: return_value=fake_result, ) as mock_execute, patch( - "litellm.proxy._experimental.mcp_server.rest_endpoints._fire_mcp_success_logging", + "litellm.proxy._experimental.mcp_server.rest_endpoints._fire_mcp_tool_call_logging", new_callable=AsyncMock, side_effect=RuntimeError("logging failed"), ) as mock_fire_logging, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py index e114f46e866..3d9afd8f250 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py @@ -1559,7 +1559,7 @@ class TestCallToolRestAPI: fire_logging = AsyncMock(side_effect=RuntimeError("logging failed")) monkeypatch.setattr( rest_endpoints, - "_fire_mcp_success_logging", + "_fire_mcp_tool_call_logging", fire_logging, raising=False, ) @@ -1590,13 +1590,13 @@ class TestCallToolRestAPI: fire_logging = AsyncMock(side_effect=asyncio.CancelledError()) monkeypatch.setattr( rest_endpoints, - "_fire_mcp_success_logging", + "_fire_mcp_tool_call_logging", fire_logging, raising=False, ) with pytest.raises(asyncio.CancelledError): - await rest_endpoints._safe_fire_mcp_success_logging( + await rest_endpoints._safe_fire_mcp_tool_call_logging( object(), {"result": "ok"}, datetime.now(), datetime.now() ) From c2d8a17692cb4dbaacccf9ecb3d678a8e4788db8 Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Wed, 8 Jul 2026 10:01:41 -0700 Subject: [PATCH 010/399] test(responses): replace perma-skip azure shell e2e with offline coverage (#32444) --- .../base_responses_api.py | 9 +- .../test_azure_responses_api.py | 5 - .../azure_shell_tool.json | 14 ++ .../test_responses_api_request_body.py | 150 ++++++++++++++---- 4 files changed, 142 insertions(+), 36 deletions(-) create mode 100644 tests/test_litellm/expected_responses_api_request/azure_shell_tool.json diff --git a/tests/llm_responses_api_testing/base_responses_api.py b/tests/llm_responses_api_testing/base_responses_api.py index 7d2e30f8372..407091a65b3 100644 --- a/tests/llm_responses_api_testing/base_responses_api.py +++ b/tests/llm_responses_api_testing/base_responses_api.py @@ -746,7 +746,8 @@ class BaseResponsesAPITest(ABC): E2E test for Shell tool on OpenAI Responses API. Passes tools=[{"type": "shell", "environment": {"type": "container_auto"}}]; validates that the request is accepted and returns a valid response. - Only runs for OpenAI/Azure (Responses API with shell support). + Only runs for OpenAI; offline coverage for the Azure route lives in + tests/test_litellm/responses/test_responses_api_request_body.py. """ base_completion_call_args = self.get_base_completion_call_args() model = ( @@ -754,8 +755,10 @@ class BaseResponsesAPITest(ABC): or base_completion_call_args.get("model") or "" ) - if "openai/" not in str(model) and "azure/" not in str(model): - pytest.skip("Shell tool e2e is only run for OpenAI/Azure Responses API") + if "openai/" not in str(model): + pytest.skip( + "Shell tool e2e is OpenAI-only; no Azure deployment supports the shell tool yet, re-enable once one exists" + ) tools = [{"type": "shell", "environment": {"type": "container_auto"}}] input_msg = "List files in /mnt/data and show python --version." try: diff --git a/tests/llm_responses_api_testing/test_azure_responses_api.py b/tests/llm_responses_api_testing/test_azure_responses_api.py index fed9e9e11f0..ccef8cbf1e7 100644 --- a/tests/llm_responses_api_testing/test_azure_responses_api.py +++ b/tests/llm_responses_api_testing/test_azure_responses_api.py @@ -2,7 +2,6 @@ import os import sys import pytest import asyncio -from typing import Optional from unittest.mock import patch, AsyncMock sys.path.insert(0, os.path.abspath("../..")) @@ -30,10 +29,6 @@ class TestAzureResponsesAPITest(BaseResponsesAPITest): "api_version": "2025-03-01-preview", } - def get_advanced_model_for_shell_tool(self) -> Optional[str]: - """If specified, overrides the model used by test_responses_api_shell_tool_streaming_sees_shell_output (e.g. openai/gpt-5.2 for shell support).""" - return "azure/gpt-5-mini" - @pytest.mark.asyncio async def test_azure_responses_api_preview_api_version(): diff --git a/tests/test_litellm/expected_responses_api_request/azure_shell_tool.json b/tests/test_litellm/expected_responses_api_request/azure_shell_tool.json new file mode 100644 index 00000000000..b716c518106 --- /dev/null +++ b/tests/test_litellm/expected_responses_api_request/azure_shell_tool.json @@ -0,0 +1,14 @@ +{ + "model": "gpt-5-mini", + "input": "List files in /mnt/data and run python --version.", + "tools": [ + { + "type": "shell", + "environment": { + "type": "container_auto" + } + } + ], + "tool_choice": "auto", + "max_output_tokens": 256 +} diff --git a/tests/test_litellm/responses/test_responses_api_request_body.py b/tests/test_litellm/responses/test_responses_api_request_body.py index e312a11e893..c39ba75bd97 100644 --- a/tests/test_litellm/responses/test_responses_api_request_body.py +++ b/tests/test_litellm/responses/test_responses_api_request_body.py @@ -1,6 +1,7 @@ """ Test that litellm.responses() / litellm.aresponses() send the expected request body -over the wire. Expected JSON bodies are stored in expected_responses_api_request/. +over the wire and surface provider errors correctly. Expected JSON bodies are stored +in expected_responses_api_request/. """ import json @@ -18,24 +19,20 @@ def _expected_dir() -> Path: return Path(__file__).resolve().parent.parent / "expected_responses_api_request" -@pytest.mark.asyncio -async def test_aresponses_context_management_and_shell_request_body_matches_expected(): - """ - Call litellm.aresponses() with context_management and shell tool; - assert the httpx POST request body matches the expected JSON. - """ - expected_path = _expected_dir() / "context_management_and_shell.json" +def _load_expected_body(filename: str) -> dict: + expected_path = _expected_dir() / filename assert expected_path.exists(), f"Expected file not found: {expected_path}" with open(expected_path) as f: - expected_body = json.load(f) + return json.load(f) - # Minimal Responses API response so parsing succeeds - mock_response = { - "id": "resp_ctx_shell_test", + +def _minimal_responses_api_payload(response_id: str, model: str) -> dict: + return { + "id": response_id, "object": "response", "created_at": 1734366691, "status": "completed", - "model": "gpt-4o", + "model": model, "output": [ { "type": "message", @@ -69,21 +66,41 @@ async def test_aresponses_context_management_and_shell_request_body_matches_expe "user": None, } - class MockResponse: - def __init__(self, json_data, status_code=200): - self._json_data = json_data - self.status_code = status_code - self.text = json.dumps(json_data) - self.headers = httpx.Headers({}) - def json(self): - return self._json_data +class MockResponse: + def __init__(self, json_data, status_code=200): + self._json_data = json_data + self.status_code = status_code + self.text = json.dumps(json_data) + self.headers = httpx.Headers({}) + + def json(self): + return self._json_data + + +def _assert_request_body_matches(request_body: dict, expected_body: dict) -> None: + for key, expected_value in expected_body.items(): + assert key in request_body, f"Missing key in request body: {key}" + assert ( + request_body[key] == expected_value + ), f"Mismatch for key {key}: got {request_body[key]!r}, expected {expected_value!r}" + + +@pytest.mark.asyncio +async def test_aresponses_context_management_and_shell_request_body_matches_expected(): + """ + Call litellm.aresponses() with context_management and shell tool; + assert the httpx POST request body matches the expected JSON. + """ + expected_body = _load_expected_body("context_management_and_shell.json") with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", new_callable=AsyncMock, ) as mock_post: - mock_post.return_value = MockResponse(mock_response, 200) + mock_post.return_value = MockResponse( + _minimal_responses_api_payload("resp_ctx_shell_test", "gpt-4o"), 200 + ) await litellm.aresponses( model="openai/gpt-4o", @@ -95,10 +112,87 @@ async def test_aresponses_context_management_and_shell_request_body_matches_expe ) mock_post.assert_called_once() - request_body = mock_post.call_args.kwargs["json"] + _assert_request_body_matches(mock_post.call_args.kwargs["json"], expected_body) - for key, expected_value in expected_body.items(): - assert key in request_body, f"Missing key in request body: {key}" - assert ( - request_body[key] == expected_value - ), f"Mismatch for key {key}: got {request_body[key]!r}, expected {expected_value!r}" + +@pytest.mark.asyncio +async def test_aresponses_azure_shell_tool_request_body_matches_expected(): + """ + Call litellm.aresponses() on the Azure route with the shell tool; + assert the httpx POST request body carries the shell tool verbatim. + """ + expected_body = _load_expected_body("azure_shell_tool.json") + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new_callable=AsyncMock, + ) as mock_post: + mock_post.return_value = MockResponse( + _minimal_responses_api_payload("resp_azure_shell_test", "gpt-5-mini"), 200 + ) + + await litellm.aresponses( + model="azure/gpt-5-mini", + api_base="https://fake-resource.openai.azure.com", + api_key="fake-api-key", + api_version="2025-03-01-preview", + input=expected_body["input"], + tools=expected_body["tools"], + tool_choice=expected_body["tool_choice"], + max_output_tokens=expected_body["max_output_tokens"], + ) + + mock_post.assert_called_once() + _assert_request_body_matches(mock_post.call_args.kwargs["json"], expected_body) + + +@pytest.mark.asyncio +async def test_aresponses_azure_shell_tool_400_maps_to_bad_request_error(): + """ + Azure rejects the shell tool for unsupported deployments with a 400; + litellm must surface that as litellm.BadRequestError carrying the provider message. + """ + error_body = { + "error": { + "message": "Tool of type 'shell' is not supported with this model.", + "type": "invalid_request_error", + "param": "tools", + "code": None, + } + } + + def _raise_azure_400(*args, **kwargs): + response = httpx.Response( + status_code=400, + json=error_body, + request=httpx.Request( + "POST", + kwargs.get( + "url", + "https://fake-resource.openai.azure.com/openai/responses", + ), + ), + ) + response.raise_for_status() + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new_callable=AsyncMock, + ) as mock_post: + mock_post.side_effect = _raise_azure_400 + + with pytest.raises(litellm.BadRequestError) as excinfo: + await litellm.aresponses( + model="azure/gpt-5-mini", + api_base="https://fake-resource.openai.azure.com", + api_key="fake-api-key", + api_version="2025-03-01-preview", + input="List files in /mnt/data and run python --version.", + tools=[{"type": "shell", "environment": {"type": "container_auto"}}], + tool_choice="auto", + max_output_tokens=256, + ) + + assert excinfo.value.status_code == 400 + assert "shell" in str(excinfo.value).lower() + assert "not supported" in str(excinfo.value).lower() From f982b67d78c65d335144d54b8c7c831fcab903f3 Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Wed, 8 Jul 2026 10:36:00 -0700 Subject: [PATCH 011/399] fix(proxy): harden secret name validation for external secret manager integrations (LIT-4201) (#32092) key_alias can become the secret name used by external secret manager integrations (HashiCorp Vault, CyberArk Conjur) when store_virtual_keys is enabled. Add raise_if_unsafe_secret_name, a shared validation check applied unconditionally before a secret name reaches either integration or the /key/generate, /key/update, and /key/regenerate API boundary, independent of the existing enable_key_alias_format_validation opt-in flag. Also hardens the Vault URL builder to percent-encode reserved characters in secret_name (preserving "/" and "@"), and switches the Conjur policy body to a real YAML serializer instead of raw string interpolation. --- .../key_management_endpoints.py | 24 +++++- .../secret_managers/base_secret_manager.py | 15 ++++ .../cyberark_secret_manager.py | 8 +- .../hashicorp_secret_manager.py | 3 +- tests/litellm_utils_tests/test_cyberark.py | 77 +++++++++++++++++++ tests/litellm_utils_tests/test_hashicorp.py | 27 +++++++ .../test_key_management_endpoints.py | 29 ++++++- .../test_base_secret_manager.py | 59 ++++++++++++++ 8 files changed, 234 insertions(+), 8 deletions(-) create mode 100644 tests/test_litellm/secret_managers/test_base_secret_manager.py diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 63f4b731871..bf64f537c7f 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -111,6 +111,7 @@ from litellm.repositories.verification_token_repository import ( VerificationTokenRepository, ) from litellm.router import Router +from litellm.secret_managers.base_secret_manager import raise_if_unsafe_secret_name from litellm.secret_managers.main import get_secret from litellm.types.proxy.management_endpoints.key_management_endpoints import ( BulkUpdateKeyRequest, @@ -6291,8 +6292,13 @@ def _validate_key_alias_format(key_alias: Optional[str]) -> None: """ Validate the format of the key_alias. - Gated behind ``litellm.enable_key_alias_format_validation`` (default **False**). - When disabled, no validation is performed so existing workflows are not broken. + A baseline validation always runs, regardless of + ``litellm.enable_key_alias_format_validation``. + + The remaining charset/length rules are gated behind + ``litellm.enable_key_alias_format_validation`` (default **False**). When disabled, + only the baseline validation above is performed, so existing workflows are not + broken. Rules (when enabled): - None is OK (no alias). @@ -6300,10 +6306,20 @@ def _validate_key_alias_format(key_alias: Optional[str]) -> None: - start/end with alphanumeric - only allow a-zA-Z0-9_-/.@ """ - if not litellm.enable_key_alias_format_validation: + if key_alias is None: return - if key_alias is None: + try: + raise_if_unsafe_secret_name(key_alias) + except ValueError: + raise ProxyException( + message="Invalid key_alias", + type=ProxyErrorTypes.bad_request_error, + param="key_alias", + code=400, + ) + + if not litellm.enable_key_alias_format_validation: return if not _KEY_ALIAS_PATTERN.match(key_alias): diff --git a/litellm/secret_managers/base_secret_manager.py b/litellm/secret_managers/base_secret_manager.py index d33d76093c9..2bb8dc73138 100644 --- a/litellm/secret_managers/base_secret_manager.py +++ b/litellm/secret_managers/base_secret_manager.py @@ -1,3 +1,4 @@ +import re from abc import ABC, abstractmethod from typing import Any, Dict, Optional, Union @@ -5,6 +6,20 @@ import httpx from litellm import verbose_logger +_UNSAFE_SECRET_NAME_PATTERN = re.compile(r"(^|/)\.\.(/|$)|[\x00-\x1f\x7f-\x9f…

]") + + +def raise_if_unsafe_secret_name(secret_name: str) -> None: + """ + Validate a secret name before it is used by a secret manager integration. + + Rejects ".." only as a path segment (bounded by "/" or the start/end of the + string, e.g. "../x", "x/..", or exactly ".."), not as a plain substring, so + names like "release-1.0..2" are not rejected. + """ + if _UNSAFE_SECRET_NAME_PATTERN.search(secret_name): + raise ValueError(f"Invalid secret_name {secret_name!r}") + class BaseSecretManager(ABC): """ diff --git a/litellm/secret_managers/cyberark_secret_manager.py b/litellm/secret_managers/cyberark_secret_manager.py index faf6224757f..2b888cb85f6 100644 --- a/litellm/secret_managers/cyberark_secret_manager.py +++ b/litellm/secret_managers/cyberark_secret_manager.py @@ -4,6 +4,7 @@ from typing import Any, Dict, Optional, Union from urllib.parse import quote import httpx +import yaml import litellm from litellm._logging import verbose_logger @@ -15,7 +16,7 @@ from litellm.llms.custom_httpx.http_handler import ( ) from litellm.proxy._types import KeyManagementSystem -from .base_secret_manager import BaseSecretManager +from .base_secret_manager import BaseSecretManager, raise_if_unsafe_secret_name from .main import str_to_bool @@ -125,8 +126,11 @@ class CyberArkSecretManager(BaseSecretManager): """ # In production, we'd check if the variable exists first # For now, we'll attempt to create it and ignore if it already exists + raise_if_unsafe_secret_name(secret_name) policy_url = f"{self.conjur_addr}/policies/{self.conjur_account}/policy/root" - policy_yaml = f"- !variable {secret_name}\n" + # Use a real YAML serializer to build the scalar safely. + quoted_name = yaml.safe_dump(secret_name, default_style='"').strip() + policy_yaml = f"- !variable {quoted_name}\n" try: client = _get_httpx_client(params={"ssl_verify": self.ssl_verify}) diff --git a/litellm/secret_managers/hashicorp_secret_manager.py b/litellm/secret_managers/hashicorp_secret_manager.py index bd1b1097347..039aecb9e58 100644 --- a/litellm/secret_managers/hashicorp_secret_manager.py +++ b/litellm/secret_managers/hashicorp_secret_manager.py @@ -14,7 +14,7 @@ from litellm.llms.custom_httpx.http_handler import ( ) from litellm.proxy._types import KeyManagementSystem -from .base_secret_manager import BaseSecretManager +from .base_secret_manager import BaseSecretManager, raise_if_unsafe_secret_name class HashicorpSecretManager(BaseSecretManager): @@ -220,6 +220,7 @@ class HashicorpSecretManager(BaseSecretManager): - With custom mount: http://127.0.0.1:8200/v1/kv/data/mykey - With path prefix: http://127.0.0.1:8200/v1/secret/data/myapp/mykey """ + raise_if_unsafe_secret_name(secret_name) resolved_namespace = self._sanitize_path_component(namespace if namespace is not None else self.vault_namespace) resolved_mount = self._sanitize_path_component(mount_name if mount_name is not None else self.vault_mount_name) if resolved_mount is None: diff --git a/tests/litellm_utils_tests/test_cyberark.py b/tests/litellm_utils_tests/test_cyberark.py index 67575d3e781..71daf35a265 100644 --- a/tests/litellm_utils_tests/test_cyberark.py +++ b/tests/litellm_utils_tests/test_cyberark.py @@ -5,6 +5,7 @@ Integration test for CyberArk Conjur Secret Manager. import os import sys import pytest +import yaml from dotenv import load_dotenv load_dotenv() @@ -42,6 +43,82 @@ def create_mock_response(status_code: int, text: str = ""): return mock_response +@pytest.mark.asyncio +async def test_cyberark_write_secret_rejects_yaml_injection(): + """ + Regression test: async_write_secret must reject a secret_name that is not + safe to embed in the Conjur policy body, before any HTTP call is made. + """ + with patch("litellm.proxy.proxy_server.premium_user", True): + malicious_secret_name = "foo\n- !grant\n role: !!admin\n member: attacker" + + mock_sync_client = MagicMock() + mock_async_client = AsyncMock() + + with ( + patch( + "litellm.secret_managers.cyberark_secret_manager._get_httpx_client", + return_value=mock_sync_client, + ), + patch( + "litellm.secret_managers.cyberark_secret_manager.get_async_httpx_client", + return_value=mock_async_client, + ), + ): + cyberark_manager = CyberArkSecretManager() + + response = await cyberark_manager.async_write_secret( + secret_name=malicious_secret_name, + secret_value="sk-1234", + ) + + assert response["status"] == "error" + assert "Invalid secret_name" in response["message"] + # The malicious policy YAML must never reach the wire. + mock_sync_client.client.post.assert_not_called() + mock_async_client.post.assert_not_called() + + +@pytest.mark.parametrize( + "secret_name", + [ + "foo: bar", + "foo # bar", + "plain-alias", + "team/user@example.com", + ], +) +def test_cyberark_ensure_variable_exists_escapes_yaml_metacharacters(secret_name): + """ + Regression test: _ensure_variable_exists must escape secret_name (not just + denylist-check it) so the policy body always parses back to exactly one + '!variable' scalar node holding the untouched secret_name. + """ + with patch("litellm.proxy.proxy_server.premium_user", True): + captured = {} + + def _capture_post(url, headers=None, content=None): + captured["content"] = content + return create_mock_response(status_code=201, text="") + + mock_sync_client = MagicMock() + mock_sync_client.client.post.side_effect = _capture_post + + with patch( + "litellm.secret_managers.cyberark_secret_manager._get_httpx_client", + return_value=mock_sync_client, + ): + cyberark_manager = CyberArkSecretManager() + cyberark_manager._ensure_variable_exists(secret_name) + + policy_yaml = captured["content"] + parsed = yaml.compose(policy_yaml) + assert len(parsed.value) == 1 + node = parsed.value[0] + assert node.tag == "!variable" + assert node.value == secret_name + + @pytest.mark.asyncio async def test_cyberark_write_and_read_secret(): """ diff --git a/tests/litellm_utils_tests/test_hashicorp.py b/tests/litellm_utils_tests/test_hashicorp.py index 3bdf11ea565..9aff7ddc10e 100644 --- a/tests/litellm_utils_tests/test_hashicorp.py +++ b/tests/litellm_utils_tests/test_hashicorp.py @@ -409,6 +409,33 @@ def test_hashicorp_custom_mount_and_prefix(hashicorp_secret_manager): hashicorp_secret_manager.vault_namespace = original_namespace +@pytest.mark.parametrize( + "malicious_secret_name", + [ + "../../../other-app/creds", + "litellm/../../secret", + "foo\nbar", + "foo
bar", + "foo
bar", + "foo\x85bar", + ], +) +def test_hashicorp_get_url_rejects_path_traversal(monkeypatch, malicious_secret_name): + """ + Regression test: get_url must reject an invalid secret_name instead of + building a URL from it. + + Uses monkeypatch + a directly-constructed manager (not the shared + hashicorp_secret_manager fixture) so this runs in CI without real Vault + credentials configured; get_url performs no I/O. + """ + monkeypatch.setenv("HCP_VAULT_TOKEN", "test-token-for-get-url-only") + manager = HashicorpSecretManager() + + with pytest.raises(ValueError): + manager.get_url(malicious_secret_name) + + mock_old_vault_response = { "request_id": "80fafb6a-e96a-4c5b-29fa-ff505ac72201", "lease_id": "", diff --git a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py index d707421aeb6..2fe7725fd12 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py @@ -9023,7 +9023,7 @@ class TestValidateKeyAliasFormat: litellm.enable_key_alias_format_validation = False def test_validation_skipped_when_flag_disabled(self): - """When enable_key_alias_format_validation is False (default), no validation occurs.""" + """When enable_key_alias_format_validation is False (default), no charset/length validation occurs.""" from litellm.proxy.management_endpoints.key_management_endpoints import ( _validate_key_alias_format, ) @@ -9034,6 +9034,33 @@ class TestValidateKeyAliasFormat: _validate_key_alias_format("!invalid!") _validate_key_alias_format("a" * 256) + @pytest.mark.parametrize( + "unsafe_alias", + [ + "../../../other-app/creds", + "litellm/../../secret", + "foo\n- !grant\n role: !!admin\n member: attacker", + "foo\rbar", + "foo\x00bar", + ], + ) + def test_validate_key_alias_format_rejects_traversal_and_control_chars_even_when_flag_disabled( + self, unsafe_alias + ): + """ + Regression test: this check must reject an invalid key_alias unconditionally, + even when enable_key_alias_format_validation (the separate, opt-in charset + rule) is disabled. + """ + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _validate_key_alias_format, + ) + + with pytest.raises(ProxyException) as exc: + _validate_key_alias_format(unsafe_alias) + assert str(exc.value.code) == "400" + assert "Invalid key_alias" in str(exc.value.message) + def test_validate_key_alias_format_valid(self): from litellm.proxy.management_endpoints.key_management_endpoints import ( _validate_key_alias_format, diff --git a/tests/test_litellm/secret_managers/test_base_secret_manager.py b/tests/test_litellm/secret_managers/test_base_secret_manager.py new file mode 100644 index 00000000000..cba6a99ab7f --- /dev/null +++ b/tests/test_litellm/secret_managers/test_base_secret_manager.py @@ -0,0 +1,59 @@ +""" +Test raise_if_unsafe_secret_name, the shared guard applied before secret_name +reaches a secret manager backend. +""" + +import os +import sys + +import pytest + +sys.path.insert(0, os.path.abspath("../..")) # Adds the parent directory to the system path + +from litellm.secret_managers.base_secret_manager import raise_if_unsafe_secret_name + + +@pytest.mark.parametrize( + "secret_name", + [ + "..", + "../../../other-app/creds", + "litellm/../../secret", + "foo/../bar", + "foo/..", + "../foo", + "foo\nbar", + "foo\rbar", + "foo\x00bar", + "foo\x7fbar", + "foo\x85bar", + "foo
bar", + "foo
bar", + ], +) +def test_raise_if_unsafe_secret_name_rejects_traversal_and_line_breaks(secret_name): + with pytest.raises(ValueError): + raise_if_unsafe_secret_name(secret_name) + + +@pytest.mark.parametrize( + "secret_name", + [ + "plain-alias", + "my-key-123", + "prod/my-service-key", + "team/user@example.com", + "foo: bar", + "foo # bar", + "foo?evil=1", + "foo#bar", + "a" * 500, + "release-1.0..2", + "my..key", + "..foo", + "foo..", + "v2.0..1-beta", + ], +) +def test_raise_if_unsafe_secret_name_allows_legitimate_aliases(secret_name): + raise_if_unsafe_secret_name(secret_name) From c0327cded4f3631e09adf63ad8b488f1333a7ac1 Mon Sep 17 00:00:00 2001 From: David Katz Date: Wed, 8 Jul 2026 09:39:48 -0400 Subject: [PATCH 012/399] fix(mcp): pair token-endpoint client_secret with the same source as client_id On re-auth against a server with a persisted DCR client, register_client_with_server short-circuits and returns a placeholder client_secret ("dummy") that the browser echoes back to /token. exchange_token_with_server overrode the caller's client_id with the persisted one but still fell back to the caller's secret when the server had none stored, so a persisted public PKCE client (which has no secret) was paired with the literal string "dummy" and the IdP rejected the exchange with 401 on every re-authorization; the proxy surfaced that as a 500. First connects and brand-new servers worked because a real DCR registration ran and no placeholder existed. Resolve the secret from the server whenever the server's client_id wins, so a secretless public client sends no client_secret at all --- .../mcp_server/discoverable_endpoints.py | 6 +- .../mcp_server/test_discoverable_endpoints.py | 59 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index fa1f73cea77..d4dbee37cdc 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -581,8 +581,12 @@ async def exchange_token_with_server( if mcp_server.token_url is None: raise HTTPException(status_code=400, detail="MCP server token url is not set") + # The id and secret must come from the same source. When the server-side client_id wins, + # falling back to the caller's secret pairs the persisted client with a foreign secret; the + # register short-circuit hands clients a placeholder secret ("dummy"), so a re-auth against a + # persisted public PKCE client (no stored secret) would send that placeholder and the IdP 401s. resolved_client_id = mcp_server.client_id if mcp_server.client_id else client_id - resolved_client_secret = mcp_server.client_secret if mcp_server.client_secret else client_secret + resolved_client_secret = mcp_server.client_secret if mcp_server.client_id else client_secret try: client_auth = build_token_endpoint_client_auth( auth_method=mcp_server.token_endpoint_auth_method, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index c808b17678a..19d030f17c4 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4156,3 +4156,62 @@ async def test_store_per_user_token_server_side_skips_invalidate_when_db_write_f invalidate_mock.assert_not_awaited() cache_set_mock.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_token_exchange_pairs_client_secret_with_server_client_id(): + """Re-auth regression: the register short-circuit hands the browser a placeholder + ``client_secret: "dummy"``, which the browser echoes back to /token. The server-side + persisted client_id wins the resolution, so the secret must come from the same (server) + source; pairing the persisted public PKCE client (no stored secret) with the caller's + placeholder makes the IdP reject the exchange with 401 on every re-auth.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="srv-1", + name="srv-1", + server_name="srv-1", + alias="srv-1", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id="persisted-client", + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.raise_for_status = MagicMock() + mock_response.json.return_value = {"access_token": "at", "token_type": "Bearer"} + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ): + await exchange_token_with_server( + request=mock_request, + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri="https://litellm.example.com/ui/mcp/oauth/callback", + client_id="srv-1", + client_secret="dummy", + code_verifier="verifier", + ) + + sent = mock_async_client.post.call_args.kwargs["data"] + assert sent["client_id"] == "persisted-client" + assert "client_secret" not in sent From 33aaea363c13e8cc576f1732673308349945e7f9 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 8 Jul 2026 10:45:20 -0700 Subject: [PATCH 013/399] ci: add OSS daily branch workflow --- .github/workflows/create_daily_oss_branch.yml | 61 ++++++++++ .github/workflows/oss_daily_guardrails.yml | 109 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 .github/workflows/create_daily_oss_branch.yml create mode 100644 .github/workflows/oss_daily_guardrails.yml diff --git a/.github/workflows/create_daily_oss_branch.yml b/.github/workflows/create_daily_oss_branch.yml new file mode 100644 index 00000000000..43de4a0e75f --- /dev/null +++ b/.github/workflows/create_daily_oss_branch.yml @@ -0,0 +1,61 @@ +name: Create Daily OSS Branch + +on: + schedule: + - cron: "0 16 * * 1-5" # 9am PT during daylight saving time, weekdays. + workflow_dispatch: + inputs: + date: + description: "Branch date in YYYY_MM_DD format. Defaults to today's UTC date." + required: false + type: string + +permissions: + contents: write + +jobs: + create-oss-branch: + if: github.repository == 'BerriAI/litellm' + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Create dated OSS branch + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REQUESTED_DATE: ${{ inputs.date }} + run: | + set -euo pipefail + + if [ -n "${REQUESTED_DATE}" ]; then + if ! echo "${REQUESTED_DATE}" | grep -Eq '^[0-9]{4}_[0-9]{2}_[0-9]{2}$'; then + echo "::error::date must use YYYY_MM_DD format, got '${REQUESTED_DATE}'" + exit 1 + fi + BRANCH_DATE="${REQUESTED_DATE}" + else + BRANCH_DATE="$(date -u +'%Y_%m_%d')" + fi + + BRANCH_NAME="litellm_oss_daily_${BRANCH_DATE}" + echo "Creating branch: ${BRANCH_NAME}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git fetch origin main "${BRANCH_NAME}" || true + + if git show-ref --verify --quiet "refs/remotes/origin/${BRANCH_NAME}"; then + echo "Branch ${BRANCH_NAME} already exists. Skipping creation." + exit 0 + fi + + git checkout -b "${BRANCH_NAME}" origin/main + git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "${BRANCH_NAME}" + echo "Successfully created and pushed branch: ${BRANCH_NAME}" diff --git a/.github/workflows/oss_daily_guardrails.yml b/.github/workflows/oss_daily_guardrails.yml new file mode 100644 index 00000000000..173b7cd4e41 --- /dev/null +++ b/.github/workflows/oss_daily_guardrails.yml @@ -0,0 +1,109 @@ +name: OSS Daily Guardrails + +on: + push: + branches: + - "litellm_oss_daily_20*" + pull_request: + branches: + - "litellm_oss_daily_20*" + - litellm_internal_staging + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + sensitive-file-guard: + name: Block sensitive OSS daily changes + if: startsWith(github.ref_name, 'litellm_oss_daily_20') || startsWith(github.head_ref, 'litellm_oss_daily_20') || startsWith(github.base_ref, 'litellm_oss_daily_20') + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + + - name: Check for sensitive file changes + env: + EVENT_NAME: ${{ github.event_name }} + BASE_REF_NAME: ${{ github.base_ref }} + HEAD_REF_NAME: ${{ github.head_ref }} + run: | + set -euo pipefail + + if [ "${EVENT_NAME}" = "pull_request" ] && echo "${HEAD_REF_NAME}" | grep -Eq '^litellm_oss_daily_20[0-9]{2}_[0-9]{2}_[0-9]{2}$'; then + # Final daily OSS branch PR into staging: review only the OSS delta + # accumulated on top of main, not unrelated main/staging drift. + BASE_REF="origin/main" + git fetch origin main + elif [ "${EVENT_NAME}" = "pull_request" ]; then + # PR targeting the daily OSS branch: review the incoming PR delta. + BASE_REF="origin/${BASE_REF_NAME}" + git fetch origin "${BASE_REF_NAME}" + else + # Push to the daily OSS branch: review the accumulated OSS delta. + BASE_REF="origin/main" + git fetch origin main + fi + + CHANGED_FILES="$(git diff --name-only "${BASE_REF}...HEAD")" + + if [ -z "${CHANGED_FILES}" ]; then + echo "No changed files detected." + exit 0 + fi + + echo "Changed files:" + echo "${CHANGED_FILES}" + + BLOCKED_FILES="$( + echo "${CHANGED_FILES}" | grep -E '(^\.github/workflows/|^\.github/actions/|^\.circleci/|^\.cursor/|^Dockerfile$|(^|/)Dockerfile$|^Makefile$|(^|/)Makefile$|(^|/)pyproject\.toml$|(^|/)uv\.lock$|(^|/)poetry\.lock$|(^|/)requirements[^/]*\.txt$|(^|/)package\.json$|(^|/)package-lock\.json$|(^|/)pnpm-lock\.yaml$|(^|/)yarn\.lock$|(^|/)tsconfig[^/]*\.json$|(^|/)(ruff|mypy|pytest|eslint|prettier|vitest|next|vite|jest)\.config\.)' || true + )" + + if [ -n "${BLOCKED_FILES}" ]; then + echo "::error::OSS daily branch contains sensitive workflow, config, dependency, or lockfile changes. Split these into a separate internal/security-reviewed PR." + echo "${BLOCKED_FILES}" + exit 1 + fi + + echo "No sensitive OSS daily file changes detected." + + oss-safe-checks: + name: Run OSS daily safe checks + if: startsWith(github.ref_name, 'litellm_oss_daily_20') || startsWith(github.head_ref, 'litellm_oss_daily_20') || startsWith(github.base_ref, 'litellm_oss_daily_20') + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + + - name: Run secret scan test + run: | + uv run --frozen --with 'pytest==9.0.2' pytest tests/litellm/test_no_hardcoded_secrets.py -v + + - name: Run Ruff + run: | + uv sync --frozen + cd litellm + uv run --no-sync ruff check . From ad69d6f3f924a7619deab91d7d3d40f391a29854 Mon Sep 17 00:00:00 2001 From: ishaan-berri <155045088+ishaan-berri@users.noreply.github.com> Date: Wed, 8 Jul 2026 11:06:56 -0700 Subject: [PATCH 014/399] test: emit e2e coverage lines for loki (#32513) --- tests/e2e/CLAUDE.md | 4 +-- tests/e2e/coverage_registry/README.md | 25 +++++++++++----- tests/e2e/coverage_registry/collector.py | 29 +++++++++++++++---- tests/e2e/coverage_registry/schema.py | 15 ++++++++++ tests/e2e/coverage_registry/test_collector.py | 26 +++++++++++++++++ 5 files changed, 83 insertions(+), 16 deletions(-) diff --git a/tests/e2e/CLAUDE.md b/tests/e2e/CLAUDE.md index a4c507ca5ea..502c881c764 100644 --- a/tests/e2e/CLAUDE.md +++ b/tests/e2e/CLAUDE.md @@ -63,7 +63,7 @@ The harness is fully typed and new code must not add `Any` or widen the basedpyr The set of tests we want is a registry checked into this repo, one row per behavior; that file is the definition of done and the denominator. Each e2e test declares what it covers with `@pytest.mark.covers("...")`, and a small collector diffs the registry against the tests and ships coverage to the existing Grafana. No Allure, no new dependencies -Coverage is organized as module > feature > test. Dashboard modules are Core LLMs, Non-Core LLMs, MCPs, Management/UI, Reliability & Performance, Logging & Guardrails, and Other. A feature is either an endpoint (`/chat/completions`) or a behavior (fallbacks, rate limits; config-driven, with no route of its own). A cell reads like `llm.chat_completions.bedrock_converse.tool_use.stream.works` +Coverage is organized as module > feature > test. Dashboard modules are `Core LLMs`, `Non-Core LLMs`, `MCPs`, `Management/UI`, `Reliability & Performance`, `Logging & Guardrails`, and `Other`. The Loki stdout formatter maps those display modules to log-safe labels (`core_llms`, `non_core_llms`, `mcp`, `management_ui`, `reliability_performance`, `logging_guardrails`, and `other`) without changing JSON or Prometheus labels. A feature is either an endpoint (`/chat/completions`) or a behavior (fallbacks, rate limits; config-driven, with no route of its own). A cell reads like `llm.chat_completions.bedrock_converse.tool_use.stream.works` The metric is coverage: the share of registry rows that have a passing covering test, reported to Grafana per module so a gap surfaces as an uncovered row rather than a silent absence @@ -71,7 +71,7 @@ Tests do not declare a dashboard module directly. They only declare the registry ### Naming grammar per module -LLMs - endpoint features (subject = the route), seeded from the Claude Code compat matrix. `chat_completions`, `messages`, and `responses` are Core LLMs. Other LLM endpoints, including `batches` and `realtime`, roll up as Non-Core LLMs. +LLMs - endpoint features (subject = the route), seeded from the Claude Code compat matrix. `chat_completions`, `messages`, and `responses` roll up to `Core LLMs`. Other LLM endpoints, including `batches` and `realtime`, roll up to `Non-Core LLMs`. ``` llm..... diff --git a/tests/e2e/coverage_registry/README.md b/tests/e2e/coverage_registry/README.md index 4177cba7766..863ce34694d 100644 --- a/tests/e2e/coverage_registry/README.md +++ b/tests/e2e/coverage_registry/README.md @@ -9,18 +9,18 @@ note; the naming grammar lives in `tests/e2e/CLAUDE.md`. A **cell** is one customer-noticeable behavior a single e2e test can assert pass/fail on, for example `llm.chat_completions.bedrock_converse.tool_use.stream.works`. Cells are -grouped `module > feature > test`, with LLM cells split into Core LLMs and Non-Core -LLMs for dashboarding. Each cell carries a tier (P0/P1/P2), a source, and a +grouped `module > feature > test`, with LLM cells split into `Core LLMs` and +`Non-Core LLMs` for dashboarding. Each cell carries a tier (P0/P1/P2), a source, and a `fail_before_fix` flag. The rows live in per-prefix YAML files (`llm_*.yaml`, `mgmt.yaml`, `mcp.yaml`, `reliability.yaml`, `logging.yaml`, `guardrail.yaml`, `other.yaml`) and validate against the discriminated union in `schema.py`, so an LLM row cannot carry a guardrail field and vice versa. `llm` rows with `subject_endpoint` of `chat_completions`, `messages`, or -`responses` roll up to "Core LLMs"; all other LLM endpoints roll up to "Non-Core -LLMs". LLM endpoint, route, and capability values are typed in `schema.py`, so new -taxonomy values require an explicit schema change. `logging` and `guardrail` are two -id-prefixes that roll up into the single "Logging & Guardrails" dashboard module. +`responses` roll up to `Core LLMs`; all other LLM endpoints roll up to `Non-Core LLMs`. +LLM endpoint, route, and capability values are typed in `schema.py`, so new taxonomy +values require an explicit schema change. `logging` and `guardrail` are two id-prefixes +that roll up into the single `Logging & Guardrails` dashboard module. A test declares what it covers with a marker: @@ -40,8 +40,17 @@ proxy. Whether a covered cell currently passes or fails is a separate, live conc cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector ``` -Use `--format prometheus` or `--format json` for CI jobs that publish coverage to -Grafana. +Use `--format loki` after the e2e pytest run in the same Kubernetes job/pod to print +structured stdout lines for Loki: + +``` +cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector --format loki --strict +``` + +This emits exactly one `COVERAGE_TOTAL` line and one `COVERAGE_MODULE` line per module +in `MODULE_ORDER`, in that order. Loki uses log-safe `module=` labels from +`LOKI_MODULE_LABELS` (`core_llms`, `management_ui`, etc.) so existing JSON and +Prometheus consumers keep their human-readable module names unchanged. The headline is overall coverage. The collector also lists markers that point at ids not in the registry, so a typo or an unenumerated behavior surfaces instead of being diff --git a/tests/e2e/coverage_registry/collector.py b/tests/e2e/coverage_registry/collector.py index 3b577106605..f6e59ca4a88 100644 --- a/tests/e2e/coverage_registry/collector.py +++ b/tests/e2e/coverage_registry/collector.py @@ -21,7 +21,7 @@ from pathlib import Path import pytest from .registry import load_registry -from .schema import MODULE_ORDER, Cell, Tier, dashboard_module +from .schema import MODULE_ORDER, Cell, Tier, dashboard_module, loki_module_label E2E_DIR = Path(__file__).resolve().parent.parent @@ -239,13 +239,31 @@ def render_prometheus(report: CoverageReport) -> str: return "\n".join(lines) +def render_loki(report: CoverageReport) -> str: + lines = [ + ( + f"COVERAGE_TOTAL percent={report.coverage_percent:.1f} " + f"covered={report.covered} total={report.total}" + ) + ] + lines.extend( + ( + f"COVERAGE_MODULE module={loki_module_label(module.module)} " + f"percent={module.coverage_percent:.1f} " + f"covered={module.covered} total={module.total}" + ) + for module in report.modules + ) + return "\n".join(lines) + + def main() -> int: parser = ArgumentParser() parser.add_argument( "--format", - choices=("text", "json", "prometheus"), + choices=("text", "json", "prometheus", "loki"), default="text", - help="Output format. Use prometheus or json for Grafana ingestion jobs.", + help="Output format. Use loki for structured stdout lines in the e2e job.", ) parser.add_argument( "--strict", @@ -265,9 +283,8 @@ def main() -> int: "text": render, "json": render_json, "prometheus": render_prometheus, - }[ - args.format - ](report) + "loki": render_loki, + }[args.format](report) print(output) # noqa: T201 # CLI entrypoint output if args.strict and report.orphan_markers: return 1 diff --git a/tests/e2e/coverage_registry/schema.py b/tests/e2e/coverage_registry/schema.py index 2e2a00e78ba..bb27fbf0ea0 100644 --- a/tests/e2e/coverage_registry/schema.py +++ b/tests/e2e/coverage_registry/schema.py @@ -157,6 +157,16 @@ MODULE_ORDER: tuple[str, ...] = ( "Other", ) +LOKI_MODULE_LABELS: dict[str, str] = { + "Core LLMs": "core_llms", + "Non-Core LLMs": "non_core_llms", + "MCPs": "mcp", + "Management/UI": "management_ui", + "Reliability & Performance": "reliability_performance", + "Logging & Guardrails": "logging_guardrails", + "Other": "other", +} + def dashboard_module(cell: Cell) -> str: """Return the Grafana/reporting module for a registry cell.""" @@ -165,3 +175,8 @@ def dashboard_module(cell: Cell) -> str: return "Core LLMs" return "Non-Core LLMs" return PREFIX_ROLLUP[cell.module] + + +def loki_module_label(module: str) -> str: + """Return the log-safe Loki label for a dashboard module.""" + return LOKI_MODULE_LABELS[module] diff --git a/tests/e2e/coverage_registry/test_collector.py b/tests/e2e/coverage_registry/test_collector.py index 355bc52730d..079ee215866 100644 --- a/tests/e2e/coverage_registry/test_collector.py +++ b/tests/e2e/coverage_registry/test_collector.py @@ -15,6 +15,7 @@ from coverage_registry.collector import ( compute_coverage, render, render_json, + render_loki, render_prometheus, ) from coverage_registry.registry import load_registry @@ -24,6 +25,7 @@ from coverage_registry.schema import ( LlmEndpoint, LoggingCell, Tier, + loki_module_label, ) @@ -149,6 +151,30 @@ def test_prometheus_render_exposes_module_coverage_timeseries() -> None: assert "litellm_e2e_coverage_orphan_markers 0" in metrics +def test_loki_render_exposes_exact_stdout_lines_for_loki() -> None: + report = compute_coverage( + (_llm("llm.chat", Tier.P0), _llm("llm.batches", Tier.P0, "batches")), + frozenset({"llm.chat"}), + ) + + lines = render_loki(report).splitlines() + + assert len(lines) == 1 + len(report.modules) + assert lines[0] == "COVERAGE_TOTAL percent=50.0 covered=1 total=2" + assert ( + lines[1] == "COVERAGE_MODULE module=core_llms percent=100.0 covered=1 total=1" + ) + assert ( + lines[2] == "COVERAGE_MODULE module=non_core_llms percent=0.0 covered=0 total=1" + ) + assert [line.split("module=", 1)[1].split(" ", 1)[0] for line in lines[1:]] == [ + loki_module_label(module.module) for module in report.modules + ] + assert all( + " " not in line.split("module=", 1)[1].split(" ", 1)[0] for line in lines[1:] + ) + + def test_real_registry_loads_and_ids_are_unique() -> None: cells = load_registry() ids = [c.id for c in cells] From 93c047d52eaa665b30931aa4ca9bf7230c0ed74d Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 11:07:58 -0700 Subject: [PATCH 015/399] feat(proxy): make Microsoft Graph endpoint configurable for GCC High (LIT-4282) (#32517) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com> --- litellm/proxy/management_endpoints/ui_sso.py | 22 ++++-- .../proxy/management_endpoints/test_ui_sso.py | 72 +++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index dbf514d2298..43fdd3ed05a 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -113,7 +113,7 @@ from litellm.proxy.utils import ( from litellm.repositories.table_repositories import SSOConfigRepository from litellm.repositories.team_repository import TeamRepository from litellm.repositories.user_repository import UserRepository -from litellm.secret_managers.main import get_secret_bool, str_to_bool +from litellm.secret_managers.main import get_secret_bool, get_secret_str, str_to_bool from litellm.types.proxy.management_endpoints.ui_sso import * # noqa: F403 from litellm.types.proxy.management_endpoints.ui_sso import ( DefaultTeamSSOParams, @@ -3737,8 +3737,7 @@ class MicrosoftSSOHandler: Handles Microsoft SSO callback response and returns a CustomOpenID object """ - graph_api_base_url = "https://graph.microsoft.com/v1.0" - graph_api_user_groups_endpoint = f"{graph_api_base_url}/me/memberOf" + DEFAULT_GRAPH_API_BASE_URL = "https://graph.microsoft.com/v1.0" """ Constants @@ -3748,6 +3747,19 @@ class MicrosoftSSOHandler: # used for debugging to show the user groups litellm found from Graph API GRAPH_API_RESPONSE_KEY = "graph_api_user_groups" + @staticmethod + def get_graph_api_base_url() -> str: + """ + Returns the Microsoft Graph API base URL, configurable via the + `MICROSOFT_GRAPH_ENDPOINT` env var so non-default clouds such as Azure + Government (GCC High) can point at `https://graph.microsoft.us/v1.0` + """ + return get_secret_str("MICROSOFT_GRAPH_ENDPOINT") or MicrosoftSSOHandler.DEFAULT_GRAPH_API_BASE_URL + + @staticmethod + def get_graph_api_user_groups_endpoint() -> str: + return f"{MicrosoftSSOHandler.get_graph_api_base_url()}/me/memberOf" + @staticmethod async def get_microsoft_callback_response( request: Request, @@ -3924,7 +3936,7 @@ class MicrosoftSSOHandler: # Fetch user membership from Microsoft Graph API all_group_ids = [] - next_link: Optional[str] = MicrosoftSSOHandler.graph_api_user_groups_endpoint + next_link: Optional[str] = MicrosoftSSOHandler.get_graph_api_user_groups_endpoint() auth_headers = {"Authorization": f"Bearer {access_token}"} page_count = 0 @@ -4007,7 +4019,7 @@ class MicrosoftSSOHandler: Users use Enterprise Applications to manage Groups and Users on Microsoft Entra ID """ - base_url = "https://graph.microsoft.com/v1.0" + base_url = MicrosoftSSOHandler.get_graph_api_base_url() # Endpoint to get app role assignments for the given service principal endpoint = f"/servicePrincipals/{service_principal_id}/appRoleAssignedTo" url = base_url + endpoint diff --git a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py index 32229e3e64e..642f20906a0 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -389,6 +389,78 @@ async def test_get_user_groups_error_handling(): assert len(result) == 0 +@pytest.mark.asyncio +async def test_get_user_groups_uses_default_graph_endpoint(monkeypatch): + monkeypatch.delenv("MICROSOFT_GRAPH_ENDPOINT", raising=False) + + requested_urls: list[str] = [] + + async def mock_get(url, *args, **kwargs): + requested_urls.append(url) + mock = MagicMock() + mock.json.return_value = {"value": []} + return mock + + with patch( + "litellm.proxy.management_endpoints.ui_sso.get_async_httpx_client" + ) as mock_client: + mock_client.return_value = MagicMock() + mock_client.return_value.get = mock_get + + await MicrosoftSSOHandler.get_user_groups_from_graph_api(access_token="mock_token") + + assert requested_urls == ["https://graph.microsoft.com/v1.0/me/memberOf"] + + +@pytest.mark.asyncio +async def test_get_user_groups_uses_configured_graph_endpoint(monkeypatch): + monkeypatch.setenv("MICROSOFT_GRAPH_ENDPOINT", "https://graph.microsoft.us/v1.0") + + requested_urls: list[str] = [] + + async def mock_get(url, *args, **kwargs): + requested_urls.append(url) + mock = MagicMock() + mock.json.return_value = {"value": []} + return mock + + with patch( + "litellm.proxy.management_endpoints.ui_sso.get_async_httpx_client" + ) as mock_client: + mock_client.return_value = MagicMock() + mock_client.return_value.get = mock_get + + await MicrosoftSSOHandler.get_user_groups_from_graph_api(access_token="mock_token") + + assert requested_urls == ["https://graph.microsoft.us/v1.0/me/memberOf"] + + +@pytest.mark.asyncio +async def test_get_group_ids_from_service_principal_uses_configured_graph_endpoint(monkeypatch): + monkeypatch.setenv("MICROSOFT_GRAPH_ENDPOINT", "https://graph.microsoft.us/v1.0") + + requested_urls: list[str] = [] + + async def mock_get(url, *args, **kwargs): + requested_urls.append(url) + mock = MagicMock() + mock.json.return_value = {"value": []} + return mock + + async_client = MagicMock() + async_client.get = mock_get + + await MicrosoftSSOHandler.get_group_ids_from_service_principal( + service_principal_id="sp-123", + async_client=async_client, + access_token="mock_token", + ) + + assert requested_urls == [ + "https://graph.microsoft.us/v1.0/servicePrincipals/sp-123/appRoleAssignedTo" + ] + + def test_get_group_ids_from_graph_api_response(): # Arrange mock_response = MicrosoftGraphAPIUserGroupResponse( From 82fd456b94b36cbfee126e0d30c549b284741a04 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 11:55:59 -0700 Subject: [PATCH 016/399] Revert "ci: skip unit test workflows when only ui or markdown files change (#32422)" This reverts commit 6df5e1b263a77a25a5bb483015fd13a79f3ef410. --- .github/workflows/test-unit-core-utils.yml | 4 ---- .github/workflows/test-unit-documentation.yml | 4 ---- .github/workflows/test-unit-enterprise-routing.yml | 4 ---- .github/workflows/test-unit-integrations.yml | 4 ---- .github/workflows/test-unit-llm-providers.yml | 4 ---- .github/workflows/test-unit-misc.yml | 4 ---- .github/workflows/test-unit-proxy-auth.yml | 4 ---- .github/workflows/test-unit-proxy-db.yml | 4 ---- .github/workflows/test-unit-proxy-endpoints.yml | 4 ---- .github/workflows/test-unit-proxy-infra.yml | 4 ---- .github/workflows/test-unit-proxy-legacy.yml | 4 ---- .github/workflows/test-unit-responses-caching-types.yml | 4 ---- 12 files changed, 48 deletions(-) diff --git a/.github/workflows/test-unit-core-utils.yml b/.github/workflows/test-unit-core-utils.yml index e563679660b..d6d6353238f 100644 --- a/.github/workflows/test-unit-core-utils.yml +++ b/.github/workflows/test-unit-core-utils.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-documentation.yml b/.github/workflows/test-unit-documentation.yml index 2c3d6e46618..4cef791a9b3 100644 --- a/.github/workflows/test-unit-documentation.yml +++ b/.github/workflows/test-unit-documentation.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-enterprise-routing.yml b/.github/workflows/test-unit-enterprise-routing.yml index 7a9b8b00f26..13136c968d1 100644 --- a/.github/workflows/test-unit-enterprise-routing.yml +++ b/.github/workflows/test-unit-enterprise-routing.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-integrations.yml b/.github/workflows/test-unit-integrations.yml index b28ba3456ce..c95ed4e7c24 100644 --- a/.github/workflows/test-unit-integrations.yml +++ b/.github/workflows/test-unit-integrations.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-llm-providers.yml b/.github/workflows/test-unit-llm-providers.yml index fecdcbd3b95..df78564ab0c 100644 --- a/.github/workflows/test-unit-llm-providers.yml +++ b/.github/workflows/test-unit-llm-providers.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-misc.yml b/.github/workflows/test-unit-misc.yml index dbc3bfc8191..7c3b195f0ad 100644 --- a/.github/workflows/test-unit-misc.yml +++ b/.github/workflows/test-unit-misc.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-proxy-auth.yml b/.github/workflows/test-unit-proxy-auth.yml index ad534cc0098..97dfaed6e81 100644 --- a/.github/workflows/test-unit-proxy-auth.yml +++ b/.github/workflows/test-unit-proxy-auth.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-proxy-db.yml b/.github/workflows/test-unit-proxy-db.yml index 35a1a9c78a0..2ac9a3b7c1c 100644 --- a/.github/workflows/test-unit-proxy-db.yml +++ b/.github/workflows/test-unit-proxy-db.yml @@ -5,10 +5,6 @@ on: branches: - main - litellm_internal_staging - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-proxy-endpoints.yml b/.github/workflows/test-unit-proxy-endpoints.yml index 7eb3d7719c0..cbb36eebdb9 100644 --- a/.github/workflows/test-unit-proxy-endpoints.yml +++ b/.github/workflows/test-unit-proxy-endpoints.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" workflow_dispatch: permissions: diff --git a/.github/workflows/test-unit-proxy-infra.yml b/.github/workflows/test-unit-proxy-infra.yml index cb944de5cf9..884d62289b9 100644 --- a/.github/workflows/test-unit-proxy-infra.yml +++ b/.github/workflows/test-unit-proxy-infra.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-proxy-legacy.yml b/.github/workflows/test-unit-proxy-legacy.yml index 9798a4e2277..8db218cd1fc 100644 --- a/.github/workflows/test-unit-proxy-legacy.yml +++ b/.github/workflows/test-unit-proxy-legacy.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read diff --git a/.github/workflows/test-unit-responses-caching-types.yml b/.github/workflows/test-unit-responses-caching-types.yml index 7331544de24..2f177587997 100644 --- a/.github/workflows/test-unit-responses-caching-types.yml +++ b/.github/workflows/test-unit-responses-caching-types.yml @@ -7,10 +7,6 @@ on: - litellm_internal_staging - litellm_oss_staging - "litellm_**" - paths-ignore: - - "ui/**" - - "**.md" - - "**.mdx" permissions: contents: read From c3dccb54cfd0666393e8874cfd007c72de8b33cc Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Wed, 8 Jul 2026 12:32:03 -0700 Subject: [PATCH 017/399] fix(health): bridge litellm_metadata into logging object in _batch_health_check (#32520) * fix(health): bridge litellm_metadata into logging object in _batch_health_check * Update litellm/litellm_core_utils/health_check_helpers.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix(health): address review - share single metadata copy, conditional api_base, add tests - Only set api_base in litellm_params when a value actually exists; providers like bedrock/vertex/gemini resolve it implicitly and an empty string overwrites their resolution. - Use a single .copy() for both metadata and litellm_metadata to prevent downstream drift between the two references. - Add 6 unit tests covering metadata bridging, api_base omission, guard conditions, and dispatch routing. Signed-off-by: pramod * refactor(health): use update_from_kwargs helper for metadata bridge Collapses the manual metadata/litellm_metadata plumbing in _batch_health_check into a single update_from_kwargs call, matching how the sibling batch/image/rerank/ocr surfaces bridge metadata onto the pre-injected logging object. Drops the bare Dict typing and the inline comment, and switches the tests to assert against the helper. --------- Signed-off-by: pramod Co-authored-by: pramod Co-authored-by: Pramod B <155433727+BPRMD18@users.noreply.github.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../health_check_helpers.py | 11 ++ .../test_health_check_helpers.py | 137 ++++++++++++++++++ 2 files changed, 148 insertions(+) diff --git a/litellm/litellm_core_utils/health_check_helpers.py b/litellm/litellm_core_utils/health_check_helpers.py index 405366382a1..42ac82abf8b 100644 --- a/litellm/litellm_core_utils/health_check_helpers.py +++ b/litellm/litellm_core_utils/health_check_helpers.py @@ -95,6 +95,17 @@ class HealthCheckHelpers: """ import litellm + logging_obj = filtered_model_params.get("litellm_logging_obj") + if logging_obj is not None: + api_base = filtered_model_params.get("api_base") + logging_obj.update_from_kwargs( + kwargs=filtered_model_params, + model=filtered_model_params.get("model"), + user=None, + optional_params={}, + litellm_params={"api_base": api_base} if api_base else None, + ) + if custom_llm_provider in LIST_BATCHES_SUPPORTED_PROVIDERS: return await litellm.alist_batches(**filtered_model_params) else: diff --git a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py index 02d72c89e80..e8ef8f15142 100644 --- a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py @@ -14,6 +14,7 @@ from litellm.constants import LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME from litellm.litellm_core_utils.health_check_helpers import HealthCheckHelpers from litellm.main import ahealth_check from litellm.proxy._types import UserAPIKeyAuth +from litellm.types.utils import LIST_BATCHES_SUPPORTED_PROVIDERS def test_update_model_params_with_health_check_tracking_information(): @@ -140,3 +141,139 @@ async def test_ahealth_check_failure_masks_raw_request_headers(): assert headers["Content-Type"] == "application/json" print(f"Masked Authorization header: {headers.get('Authorization', 'NOT FOUND')}") + + +@pytest.mark.asyncio +async def test_batch_health_check_bridges_metadata_into_logging_obj(): + """_batch_health_check must call update_from_kwargs on the pre-injected + logging object so callbacks receive identity/tracking fields in + model_call_details["litellm_params"]["metadata"].""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = { + "tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME], + "user_api_key_alias": "health-check-key", + } + + filtered_model_params = { + "model": "openai/gpt-4", + "api_base": "https://api.openai.com", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + with patch("litellm.alist_batches", new_callable=AsyncMock, return_value={}): + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="openai", + model_params={"model": "openai/gpt-4"}, + filtered_model_params=filtered_model_params, + ) + + mock_logging_obj.update_from_kwargs.assert_called_once() + call_kwargs = mock_logging_obj.update_from_kwargs.call_args[1] + assert call_kwargs["model"] == "openai/gpt-4" + assert call_kwargs["kwargs"] is filtered_model_params + assert call_kwargs["litellm_params"] == {"api_base": "https://api.openai.com"} + + +@pytest.mark.asyncio +async def test_batch_health_check_omits_api_base_when_absent(): + """api_base must not appear in litellm_params when the provider resolves + it implicitly (bedrock, vertex, gemini).""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + filtered_model_params = { + "model": "bedrock/anthropic.claude-v2", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + with patch("litellm.acompletion", new_callable=AsyncMock, return_value={}): + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="bedrock", + model_params={"model": "bedrock/anthropic.claude-v2"}, + filtered_model_params=filtered_model_params, + ) + + call_kwargs = mock_logging_obj.update_from_kwargs.call_args[1] + assert call_kwargs["litellm_params"] is None + + +@pytest.mark.asyncio +async def test_batch_health_check_skips_bridge_when_no_logging_obj(): + """When litellm_logging_obj is absent, dispatch still proceeds.""" + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + filtered_model_params = { + "model": "openai/gpt-4", + "litellm_metadata": litellm_metadata, + } + + with patch( + "litellm.alist_batches", new_callable=AsyncMock, return_value={} + ) as mock_alist: + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="openai", + model_params={"model": "openai/gpt-4"}, + filtered_model_params=filtered_model_params, + ) + mock_alist.assert_called_once() + + +@pytest.mark.asyncio +async def test_batch_health_check_uses_alist_batches_for_supported_providers(): + """Providers in LIST_BATCHES_SUPPORTED_PROVIDERS dispatch to alist_batches.""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + for provider in LIST_BATCHES_SUPPORTED_PROVIDERS: + filtered_model_params = { + "model": f"{provider}/some-model", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + with patch( + "litellm.alist_batches", new_callable=AsyncMock, return_value={} + ) as mock_alist: + await HealthCheckHelpers._batch_health_check( + custom_llm_provider=provider, + model_params={"model": f"{provider}/some-model"}, + filtered_model_params=filtered_model_params, + ) + mock_alist.assert_called_once() + + +@pytest.mark.asyncio +async def test_batch_health_check_falls_back_to_acompletion_for_unsupported(): + """Providers not in LIST_BATCHES_SUPPORTED_PROVIDERS fall back to acompletion.""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + filtered_model_params = { + "model": "bedrock/anthropic.claude-v2", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + model_params = {"model": "bedrock/anthropic.claude-v2", "messages": []} + + with ( + patch("litellm.alist_batches", new_callable=AsyncMock) as mock_alist, + patch("litellm.acompletion", new_callable=AsyncMock, return_value={}) as mock_acompletion, + ): + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="bedrock", + model_params=model_params, + filtered_model_params=filtered_model_params, + ) + mock_alist.assert_not_called() + mock_acompletion.assert_called_once_with(**model_params) From 12d1873b44286f1bb9c1e07970958b5e134354b1 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 13:23:34 -0700 Subject: [PATCH 018/399] feat(ui): add enterprise license expiry banner to admin dashboard Surfaces a persistent, tiered banner under the dashboard navbar when an airgapped enterprise license is close to expiring: an amber, session-dismissible warning within 30 days, a non-dismissible red alert within 7 days, and a non-dismissible red banner once the date has passed. It reads the existing /health/license endpoint, so no backend change is needed, and is driven strictly by expiration_date; community and remote-validated instances that report no date show nothing. Shared day-count math is extracted to licenseUtils so the banner and the existing UsageIndicator widget stay in sync --- .../hooks/license/useLicenseInfo.ts | 15 +++ .../src/app/(dashboard)/layout.tsx | 2 + .../components/LicenseExpiryBanner.test.tsx | 90 ++++++++++++++++++ .../src/components/LicenseExpiryBanner.tsx | 92 +++++++++++++++++++ .../src/components/UsageIndicator.tsx | 12 +-- .../src/utils/licenseUtils.test.ts | 61 ++++++++++++ .../src/utils/licenseUtils.ts | 49 ++++++++++ 7 files changed, 310 insertions(+), 11 deletions(-) create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts create mode 100644 ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx create mode 100644 ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx create mode 100644 ui/litellm-dashboard/src/utils/licenseUtils.test.ts create mode 100644 ui/litellm-dashboard/src/utils/licenseUtils.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts new file mode 100644 index 00000000000..f4574c36ef6 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts @@ -0,0 +1,15 @@ +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import { getLicenseInfo, LicenseInfo } from "@/components/networking"; +import { createQueryKeys } from "../common/queryKeysFactory"; + +const licenseInfoKeys = createQueryKeys("licenseInfo"); + +export const useLicenseInfo = (accessToken: string | null | undefined): UseQueryResult => { + return useQuery({ + queryKey: licenseInfoKeys.detail("license"), + queryFn: () => getLicenseInfo(accessToken!), + enabled: Boolean(accessToken), + staleTime: 5 * 60 * 1000, + retry: false, + }); +}; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx index 09951dc1923..c84209b80cf 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx @@ -8,6 +8,7 @@ import { useAuth } from "@/contexts/AuthContext"; import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider"; import { useRouter, useSearchParams, usePathname } from "next/navigation"; import { DebugWarningBanner } from "@/components/DebugWarningBanner"; +import { LicenseExpiryBanner } from "@/components/LicenseExpiryBanner"; import { MIGRATED_PAGES, migratedHref, legacyPageHref, legacyKeyForPathname } from "@/utils/migratedPages"; import { PluginModeProvider, usePluginMode } from "@/contexts/PluginModeContext"; import { createApiClient } from "@/lib/http/client"; @@ -116,6 +117,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) { onToggleSidebar={() => setSidebarCollapsed((v) => !v)} /> +
{mode !== "ai-gateway" ? (
diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx new file mode 100644 index 00000000000..d6b419ace7c --- /dev/null +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { LicenseExpiryBannerView } from "./LicenseExpiryBanner"; +import { LicenseInfo } from "./networking"; + +const daysFromNow = (n: number): string => { + const date = new Date(); + date.setUTCDate(date.getUTCDate() + n); + return date.toISOString().slice(0, 10); +}; + +const licenseWith = (expiration_date: string | null): LicenseInfo => ({ + has_license: expiration_date !== null, + license_type: expiration_date !== null ? "enterprise" : "community", + expiration_date, + allowed_features: [], + limits: { max_users: null, max_teams: null }, +}); + +describe("LicenseExpiryBannerView", () => { + beforeEach(() => { + sessionStorage.clear(); + }); + + afterEach(() => { + sessionStorage.clear(); + }); + + it("renders nothing when there is no license info", () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); + + it("renders nothing when expiration_date is null (community or remote-validated)", () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); + + it("renders nothing when expiry is more than 30 days out", () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); + + it("shows a dismissible amber warning within 30 days", () => { + const { container } = render(); + expect(screen.getByText(/expires in 20 days/)).toBeInTheDocument(); + expect(container.querySelector(".ant-alert-warning")).toBeInTheDocument(); + expect(screen.queryByRole("button")).toBeInTheDocument(); + expect(screen.getByRole("link", { name: "sales@berri.ai" })).toHaveAttribute("href", "mailto:sales@berri.ai"); + }); + + it("shows a non-dismissible red critical alert within 7 days", () => { + const { container } = render(); + expect(screen.getByText(/expires in 5 days/)).toBeInTheDocument(); + expect(container.querySelector(".ant-alert-error")).toBeInTheDocument(); + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + + it("says 'expires today' on the expiration day", () => { + render(); + expect(screen.getByText(/expires today/)).toBeInTheDocument(); + }); + + it("shows a non-dismissible red expired alert stating features are disabled", () => { + const { container } = render(); + expect(screen.getByText(/expired on/)).toBeInTheDocument(); + expect(screen.getByText(/features are now disabled/i)).toBeInTheDocument(); + expect(container.querySelector(".ant-alert-error")).toBeInTheDocument(); + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + + it("hides the warning after dismissal and stays hidden within the session", () => { + const expiration = daysFromNow(20); + const { unmount } = render(); + fireEvent.click(screen.getByRole("button")); + expect(screen.queryByText(/expires in 20 days/)).not.toBeInTheDocument(); + + unmount(); + render(); + expect(screen.queryByText(/expires in 20 days/)).not.toBeInTheDocument(); + }); + + it("still shows a critical alert even when its date was previously dismissed", () => { + const expiration = daysFromNow(5); + sessionStorage.setItem(`litellm:licenseExpiryBannerDismissed:${expiration}`, "true"); + render(); + expect(screen.getByText(/expires in 5 days/)).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx new file mode 100644 index 00000000000..e5b8a65168a --- /dev/null +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx @@ -0,0 +1,92 @@ +"use client"; + +import React, { useState } from "react"; +import { Alert } from "antd"; +import { LicenseInfo } from "@/components/networking"; +import { useLicenseInfo } from "@/app/(dashboard)/hooks/license/useLicenseInfo"; +import { formatExpiryDate, getDaysUntilExpiration, getLicenseExpiryTier } from "@/utils/licenseUtils"; + +const DISMISS_KEY_PREFIX = "litellm:licenseExpiryBannerDismissed:"; +const SALES_EMAIL = "sales@berri.ai"; + +const salesLink = {SALES_EMAIL}; + +interface LicenseExpiryBannerProps { + accessToken: string | null; +} + +interface LicenseExpiryBannerViewProps { + licenseInfo: LicenseInfo | null; +} + +const describeCountdown = (days: number): string => { + if (days <= 0) { + return "expires today"; + } + if (days === 1) { + return "expires in 1 day"; + } + return `expires in ${days} days`; +}; + +export const LicenseExpiryBannerView: React.FC = ({ licenseInfo }) => { + const [locallyDismissed, setLocallyDismissed] = useState(false); + + const expirationDate = licenseInfo?.expiration_date ?? null; + const tier = getLicenseExpiryTier(expirationDate); + const days = getDaysUntilExpiration(expirationDate); + + if (expirationDate === null || tier === "none" || days === null) { + return null; + } + + const isDismissible = tier === "warning"; + const dismissKey = `${DISMISS_KEY_PREFIX}${expirationDate}`; + const previouslyDismissed = + isDismissible && typeof window !== "undefined" ? sessionStorage.getItem(dismissKey) === "true" : false; + + if (isDismissible && (locallyDismissed || previouslyDismissed)) { + return null; + } + + const formattedDate = formatExpiryDate(expirationDate); + + const message = + tier === "expired" + ? `Your LiteLLM Enterprise license expired on ${formattedDate}` + : `Your LiteLLM Enterprise license ${describeCountdown(days)} (${formattedDate})`; + + const description = + tier === "expired" ? ( + <>Enterprise features are now disabled. Reach out to {salesLink} to restore access + ) : tier === "critical" ? ( + <>Renew now to avoid losing enterprise features. Reach out to {salesLink} + ) : ( + <>Renew before it lapses to keep enterprise features. Reach out to {salesLink} + ); + + const handleClose = () => { + if (typeof window !== "undefined") { + sessionStorage.setItem(dismissKey, "true"); + } + setLocallyDismissed(true); + }; + + return ( + + ); +}; + +export const LicenseExpiryBanner: React.FC = ({ accessToken }) => { + const { data } = useLicenseInfo(accessToken); + return ; +}; diff --git a/ui/litellm-dashboard/src/components/UsageIndicator.tsx b/ui/litellm-dashboard/src/components/UsageIndicator.tsx index 030df228de0..8165f8eb6f6 100644 --- a/ui/litellm-dashboard/src/components/UsageIndicator.tsx +++ b/ui/litellm-dashboard/src/components/UsageIndicator.tsx @@ -15,6 +15,7 @@ import { useEffect, useState } from "react"; import { getRemainingUsers, getLicenseInfo, LicenseInfo } from "./networking"; import { cn } from "@/lib/cva.config"; +import { getDaysUntilExpiration } from "@/utils/licenseUtils"; interface UsageIndicatorProps { accessToken: string | null; @@ -30,17 +31,6 @@ interface UsageData { total_teams_remaining: number | null; } -// Calculate days until expiration -const getDaysUntilExpiration = (expirationDate: string | null): number | null => { - if (!expirationDate) return null; - const expDate = new Date(expirationDate + "T00:00:00Z"); // Force UTC midnight - const now = new Date(); - now.setHours(0, 0, 0, 0); // Normalize to local midnight - const diffTime = expDate.getTime() - now.getTime(); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - return diffDays; -}; - // Format expiration for display const formatExpirationDisplay = (daysRemaining: number | null): string => { if (daysRemaining === null) return "No expiration"; diff --git a/ui/litellm-dashboard/src/utils/licenseUtils.test.ts b/ui/litellm-dashboard/src/utils/licenseUtils.test.ts new file mode 100644 index 00000000000..717b8f0d90d --- /dev/null +++ b/ui/litellm-dashboard/src/utils/licenseUtils.test.ts @@ -0,0 +1,61 @@ +import { describe, it, expect } from "vitest"; +import { type LicenseExpiryTier, formatExpiryDate, getDaysUntilExpiration, getLicenseExpiryTier } from "./licenseUtils"; + +const NOW = new Date("2026-07-08T00:00:00Z"); + +describe("getDaysUntilExpiration", () => { + it("returns null for a null expiration", () => { + expect(getDaysUntilExpiration(null, NOW)).toBeNull(); + }); + + it("returns null for an unparseable date", () => { + expect(getDaysUntilExpiration("not-a-date", NOW)).toBeNull(); + }); + + it("returns 0 for an expiration on the current UTC day", () => { + expect(getDaysUntilExpiration("2026-07-08", NOW)).toBe(0); + }); + + it("returns a positive count for future dates", () => { + expect(getDaysUntilExpiration("2026-07-15", NOW)).toBe(7); + expect(getDaysUntilExpiration("2026-08-07", NOW)).toBe(30); + }); + + it("returns a negative count for a past date", () => { + expect(getDaysUntilExpiration("2026-07-07", NOW)).toBe(-1); + }); + + it("is timezone-independent within a UTC day", () => { + expect(getDaysUntilExpiration("2026-08-07", new Date("2026-07-08T00:00:01Z"))).toBe(30); + expect(getDaysUntilExpiration("2026-08-07", new Date("2026-07-08T23:59:59Z"))).toBe(30); + }); +}); + +describe("getLicenseExpiryTier", () => { + const cases: Array<[string | null, LicenseExpiryTier]> = [ + [null, "none"], + ["not-a-date", "none"], + ["2026-08-08", "none"], + ["2026-08-07", "warning"], + ["2026-07-16", "warning"], + ["2026-07-15", "critical"], + ["2026-07-09", "critical"], + ["2026-07-08", "critical"], + ["2026-07-07", "expired"], + ["2026-01-01", "expired"], + ]; + + it.each(cases)("classifies %s as %s", (date, expected) => { + expect(getLicenseExpiryTier(date, NOW)).toBe(expected); + }); +}); + +describe("formatExpiryDate", () => { + it("formats an ISO date as a human-readable UTC date", () => { + expect(formatExpiryDate("2026-07-31")).toBe("Jul 31, 2026"); + }); + + it("returns the input unchanged when unparseable", () => { + expect(formatExpiryDate("bogus")).toBe("bogus"); + }); +}); diff --git a/ui/litellm-dashboard/src/utils/licenseUtils.ts b/ui/litellm-dashboard/src/utils/licenseUtils.ts new file mode 100644 index 00000000000..b2681664c56 --- /dev/null +++ b/ui/litellm-dashboard/src/utils/licenseUtils.ts @@ -0,0 +1,49 @@ +export type LicenseExpiryTier = "none" | "warning" | "critical" | "expired"; + +export const LICENSE_EXPIRY_WARNING_DAYS = 30; +export const LICENSE_EXPIRY_CRITICAL_DAYS = 7; + +export const getDaysUntilExpiration = (expirationDate: string | null, now: Date = new Date()): number | null => { + if (!expirationDate) { + return null; + } + + const expiration = new Date(`${expirationDate}T00:00:00Z`); + if (Number.isNaN(expiration.getTime())) { + return null; + } + + const nowUtcMidnight = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()); + const diffMs = expiration.getTime() - nowUtcMidnight; + return Math.ceil(diffMs / (1000 * 60 * 60 * 24)); +}; + +export const getLicenseExpiryTier = (expirationDate: string | null, now: Date = new Date()): LicenseExpiryTier => { + const days = getDaysUntilExpiration(expirationDate, now); + if (days === null) { + return "none"; + } + if (days < 0) { + return "expired"; + } + if (days <= LICENSE_EXPIRY_CRITICAL_DAYS) { + return "critical"; + } + if (days <= LICENSE_EXPIRY_WARNING_DAYS) { + return "warning"; + } + return "none"; +}; + +export const formatExpiryDate = (expirationDate: string): string => { + const date = new Date(`${expirationDate}T00:00:00Z`); + if (Number.isNaN(date.getTime())) { + return expirationDate; + } + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + timeZone: "UTC", + }); +}; From 85d1fe6e2a535e9edfc1ae0b0854eb204573c7ba Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Wed, 8 Jul 2026 13:44:48 -0700 Subject: [PATCH 019/399] fix(otel): restore error.* span attributes on v2 error spans (LIT-4179) (#32524) The v2 emitter has never stamped error.message / error.code / error.stack_trace / error.llm_provider as span attributes; only error.type reached the wire. Backends that flatten span attributes into label indexes (Elastic APM labels.error_*, Datadog span tags) lost these four fields when v2 became the active integration on v1.90+ for otel_v2-flagged deployments. The pre-existing exception span event carrying the full message (LIT-3758) is unchanged; the message now rides both places at once, matching v1s shape. SpanError grows three optional detail fields; _parse_error threads them from StandardLoggingPayloadErrorInformation; the emitters error branch stamps them via a new module-level helper, guarded per field so guardrail-shape errors are not polluted with empty attributes. New semconv constants mirror open_inference.ErrorAttributes byte-for-byte, so v1 and v2 consumers read the same keys. Regression tests extend the mapped test files under tests/test_litellm/integrations/otel/. pytest reports 243 passed. --- litellm/integrations/otel/__init__.py | 2 + litellm/integrations/otel/emitter.py | 35 +++++- litellm/integrations/otel/model/payloads.py | 6 + litellm/integrations/otel/model/semconv.py | 20 ++++ .../otel/test_otel_v2_components.py | 110 ++++++++++++++++-- .../otel/test_otel_v2_sources_of_truth.py | 47 +++++++- 6 files changed, 203 insertions(+), 17 deletions(-) diff --git a/litellm/integrations/otel/__init__.py b/litellm/integrations/otel/__init__.py index 7f78f7156b4..5e167e006ff 100644 --- a/litellm/integrations/otel/__init__.py +++ b/litellm/integrations/otel/__init__.py @@ -52,6 +52,7 @@ from litellm.integrations.otel.model.semconv import ( GenAIProvider, JsonRpc, LiteLLM, + LiteLLMError, MCPMethod, Metric, Network, @@ -87,6 +88,7 @@ __all__ = [ "HTTP", "JsonRpc", "LiteLLM", + "LiteLLMError", "MCP", "MCPMethod", "Metric", diff --git a/litellm/integrations/otel/emitter.py b/litellm/integrations/otel/emitter.py index 8441cbae834..46aa166a8bb 100644 --- a/litellm/integrations/otel/emitter.py +++ b/litellm/integrations/otel/emitter.py @@ -16,9 +16,10 @@ from litellm.integrations.otel.model.payloads import ( MCPListToolsSpanData, MCPToolCallSpanData, ServiceSpanData, + SpanError, ) from litellm.integrations.otel.plumbing.providers import to_otel_span_kind -from litellm.integrations.otel.model.semconv import Error, ExceptionEvent +from litellm.integrations.otel.model.semconv import Error, ExceptionEvent, LiteLLMError from litellm.integrations.otel.model.spans import ( SPAN_REGISTRY, SpanRole, @@ -49,6 +50,27 @@ _NAME_BUILDERS: dict[SpanRole, Callable[..., str]] = { _DEDUP_CACHE_MAX = 10_000 +def _stamp_otel_error_attributes(span: Span, error_type: str, resolved_message: str) -> None: + """Stamp the OTel-semconv error attributes (``error.type`` + ``error.message``). + ``error_type`` and ``resolved_message`` are ``finish_span``'s already-computed + fallback chains, so the pair on the status, event, and attributes stays in + lockstep.""" + span.set_attribute(Error.TYPE, error_type) + span.set_attribute(Error.MESSAGE, resolved_message) + + +def _stamp_litellm_error_attributes(span: Span, error: SpanError) -> None: + """Stamp litellm-specific error detail attributes. Emitted only when the + corresponding field is populated so guardrail-shape errors carrying only a + message aren't polluted with empty detail keys.""" + if error.code: + span.set_attribute(LiteLLMError.CODE, error.code) + if error.stack_trace: + span.set_attribute(LiteLLMError.STACK_TRACE, error.stack_trace) + if error.llm_provider: + span.set_attribute(LiteLLMError.LLM_PROVIDER, error.llm_provider) + + class SpanEmitter: def __init__( self, @@ -190,12 +212,13 @@ class SpanEmitter: if error and (error.error_type or error.message): error_type = error.error_type or "error" message = error.message or error.error_type or "error" - span.set_attribute(Error.TYPE, error_type) + _stamp_otel_error_attributes(span, error_type, message) + _stamp_litellm_error_attributes(span, error) span.set_status(Status(StatusCode.ERROR, message)) - # Carry the full message on the standard ``exception`` event so backends - # map it as full text under ``exception.message``. Setting it as a bare - # string attribute instead lets backends like Elasticsearch dynamic-map - # it to a ``keyword`` capped at 1024 chars, truncating the message. + # Also emit the semconv ``exception`` event so backends that + # dynamic-map unknown string span attrs to ``keyword`` (e.g. + # Elasticsearch with a 1024-char ``ignore_above``) still see the + # full untruncated message on the recognized event field. span.add_event( ExceptionEvent.NAME, {ExceptionEvent.TYPE: error_type, ExceptionEvent.MESSAGE: message}, diff --git a/litellm/integrations/otel/model/payloads.py b/litellm/integrations/otel/model/payloads.py index fcd710492f0..4a8f01858b5 100644 --- a/litellm/integrations/otel/model/payloads.py +++ b/litellm/integrations/otel/model/payloads.py @@ -141,6 +141,9 @@ class LLMCost: class SpanError: error_type: str | None = None message: str | None = None + code: str | None = None + stack_trace: str | None = None + llm_provider: str | None = None @dataclass(frozen=True) @@ -571,6 +574,9 @@ def _parse_error(payload: "StandardLoggingPayload") -> SpanError | None: return SpanError( error_type=as_str(info.get("error_class")) or as_str(info.get("error_code")), message=as_str(info.get("error_message")) or as_str(payload.get("error_str")), + code=as_str(info.get("error_code")), + stack_trace=as_str(info.get("traceback")), + llm_provider=as_str(info.get("llm_provider")), ) diff --git a/litellm/integrations/otel/model/semconv.py b/litellm/integrations/otel/model/semconv.py index 4e725ae0a29..69d1e454655 100644 --- a/litellm/integrations/otel/model/semconv.py +++ b/litellm/integrations/otel/model/semconv.py @@ -144,7 +144,27 @@ class Client: class Error: + """OTel-defined error attribute keys, from the semconv ``error.*`` registry. + ``MESSAGE`` is marked *Deprecated* upstream in favor of domain-specific + error message keys plus ``exception.message`` on the exception event, but + is still defined and stamped by litellm's v1 integration; keeping it here + for byte-for-byte parity.""" + TYPE: Final = "error.type" + MESSAGE: Final = "error.message" + + +class LiteLLMError: + """LiteLLM-specific error attribute keys. Emitted under the ``error.*`` + namespace (not ``litellm.*``) for byte-for-byte compat with the v1 + integration in ``opentelemetry.py``; consumers reading these keys on v1 + spans read the same keys on v2 spans. OTel semconv does not define any of + these three, and per its extension rules a namespace may carry additional + vendor keys as long as they don't collide with defined names.""" + + CODE: Final = "error.code" + STACK_TRACE: Final = "error.stack_trace" + LLM_PROVIDER: Final = "error.llm_provider" class ExceptionEvent: diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_components.py b/tests/test_litellm/integrations/otel/test_otel_v2_components.py index 19eef284b91..298047ec18b 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_components.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_components.py @@ -579,13 +579,10 @@ def _exception_event(span): def test_error_message_recorded_as_full_exception_event_untruncated(): - """Regression for the Elasticsearch keyword/ignore_above:1024 truncation. - - A long error message must survive intact on the standard ``exception`` - event under ``exception.message`` — not get dropped onto a bare string - attribute that backends dynamic-map to a 1024-char ``keyword``. The SDK - must not truncate it either, so a 5000-char message stays 5000 chars. - """ + """The ``exception`` event carries the full untruncated message under + ``exception.message`` so backends that dynamic-map unknown string span + attrs to ``keyword`` (e.g. Elasticsearch with a 1024-char ``ignore_above``) + still see it in full via the semconv-recognized event field.""" from litellm.integrations.otel.model.semconv import Error, ExceptionEvent long_message = "boom: " + "x" * 5000 @@ -596,13 +593,108 @@ def test_error_message_recorded_as_full_exception_event_untruncated(): assert len(event.attributes[ExceptionEvent.MESSAGE]) == len(long_message) > 1024 assert event.attributes[ExceptionEvent.TYPE] == "litellm.APIError" - # error.type stays a low-cardinality attribute; the message does NOT become a - # bare string attribute (which is what got truncated). + # error.type stays a low-cardinality attribute; the exception EVENT field + # ``exception.message`` never becomes a bare string attribute. assert span.attributes[Error.TYPE] == "litellm.APIError" assert ExceptionEvent.MESSAGE not in span.attributes assert span.status.description == long_message +def test_error_details_stamped_as_span_attributes_for_labels_ingest(): + """OTel-defined keys and litellm-specific detail keys both ride span + attributes so backends that flatten attrs into label indexes (Elastic APM + ``labels.*``, Datadog span tags) render them. The exception event with the + full untruncated message stays alongside — both places, matching v1's + shape.""" + from litellm.integrations.otel.model.semconv import Error, ExceptionEvent, LiteLLMError + from litellm.integrations.otel.emitter import SpanEmitter + + cfg = OpenTelemetryV2Config(exporter="in_memory") + provider, exporter = providers.in_memory_provider(cfg) + engine = SpanEmitter(providers.get_tracer(provider, "t"), cfg) + data = LLMCallSpanData( + operation=GenAIOperation.CHAT, + provider="openai", + request_model="gpt-4o", + response_model=None, + response_id=None, + request_params=LLMRequestParams(), + usage=LLMUsage(), + finish_reasons=(), + error=SpanError( + error_type="litellm.BadRequestError", + message="400: violated moderation policy", + code="400", + stack_trace="File proxy_server.py line 8570 ...", + llm_provider="openai", + ), + response_cost=None, + server=None, + identity=RequestIdentity(call_id=None), + ) + engine.emit(SpanRole.LLM_CALL, data) + (span,) = exporter.get_finished_spans() + + # OTel-defined keys (from the ``error.*`` semconv registry). + assert span.attributes[Error.TYPE] == "litellm.BadRequestError" + assert span.attributes[Error.MESSAGE] == "400: violated moderation policy" + # LiteLLM-specific detail keys — vendor-namespaced under ``error.*`` + # for v1-parity, not defined by OTel semconv. + assert span.attributes[LiteLLMError.CODE] == "400" + assert span.attributes[LiteLLMError.STACK_TRACE] == "File proxy_server.py line 8570 ..." + assert span.attributes[LiteLLMError.LLM_PROVIDER] == "openai" + + # The exception event carries the same message on the span too. + event = _exception_event(span) + assert event.attributes[ExceptionEvent.MESSAGE] == "400: violated moderation policy" + + +def test_error_details_omitted_when_span_error_carries_only_message(): + """A guardrail-shape error (message only, no code/traceback/provider) must + not pollute the span with empty-string detail attributes. Only the keys + that carry real data land.""" + from litellm.integrations.otel.model.semconv import Error, LiteLLMError + + span = _emit_error_span("guardrail rejected", error_type="ContentFilter") + + assert span.attributes[Error.TYPE] == "ContentFilter" + assert span.attributes[Error.MESSAGE] == "guardrail rejected" + # LiteLLM-specific detail keys aren't stamped when the SpanError doesn't + # carry them. + assert LiteLLMError.CODE not in span.attributes + assert LiteLLMError.STACK_TRACE not in span.attributes + assert LiteLLMError.LLM_PROVIDER not in span.attributes + + +def test_v2_error_attribute_keys_match_v1_error_attributes_byte_for_byte(): + """v1 (``opentelemetry.py``) and v2 (``otel/`` package) stamp identical + span-attribute keys so consumers reading ``labels.error_message`` don't + care which integration produced the span. Renaming either side is a + breaking change for downstream dashboards; this test locks the vocabulary.""" + from litellm.integrations._types.open_inference import ErrorAttributes + from litellm.integrations.otel.model.semconv import Error, LiteLLMError + + assert Error.TYPE == ErrorAttributes.ERROR_TYPE + assert Error.MESSAGE == ErrorAttributes.ERROR_MESSAGE + assert LiteLLMError.CODE == ErrorAttributes.ERROR_CODE + assert LiteLLMError.STACK_TRACE == ErrorAttributes.ERROR_STACK_TRACE + assert LiteLLMError.LLM_PROVIDER == ErrorAttributes.ERROR_LLM_PROVIDER + + +def test_error_message_falls_back_to_error_type_when_message_absent(): + """A ``SpanError(error_type=..., message=None)`` still renders on the span: + the resolved message is the error_type, and it lands on ``error.message``, + the exception event, and the span-status description in lockstep so a + single-source-of-truth view isn't inconsistent.""" + from litellm.integrations.otel.model.semconv import Error, ExceptionEvent + + span = _emit_error_span(message=None, error_type="RateLimitError") + + assert span.attributes[Error.MESSAGE] == "RateLimitError" + assert _exception_event(span).attributes[ExceptionEvent.MESSAGE] == "RateLimitError" + assert span.status.description == "RateLimitError" + + def test_success_span_records_no_exception_event(): from litellm.integrations.otel.emitter import SpanEmitter from litellm.integrations.otel.model.semconv import ExceptionEvent diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py b/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py index 834a484090f..89aa73a6066 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py @@ -144,11 +144,13 @@ def _all_constants(cls): def test_attribute_keys_are_unique_across_namespaces(): - from litellm.integrations.otel import MCP, Client, JsonRpc, Network + from litellm.integrations.otel import MCP, Client, JsonRpc, LiteLLMError, Network # prefixes are allowed to be substrings; exact keys must not collide. + # ``LiteLLMError`` shares the ``error.*`` prefix with ``Error`` by design + # (v1-parity); the assert below is the guarantee they never overlap. exact = set() - for cls in (GenAI, Error, Server, HTTP, DB, MCP, JsonRpc, Network, Client): + for cls in (GenAI, Error, LiteLLMError, Server, HTTP, DB, MCP, JsonRpc, Network, Client): for key in _all_constants(cls): assert key not in exact, f"duplicate attribute key {key}" exact.add(key) @@ -342,6 +344,47 @@ def test_llm_call_adapter_failure_path(): assert data.error.message == "429 slow down" +def test_llm_call_adapter_carries_error_detail_fields(): + """``_parse_error`` threads the full detail set from ``error_information`` + (``error_code``, ``traceback``, ``llm_provider``) onto ``SpanError`` so the + emitter can stamp them as span attributes.""" + payload = _sample_payload( + status="failure", + error_information={ + "error_class": "BadRequestError", + "error_message": "400 violated moderation policy", + "error_code": "400", + "traceback": "File proxy_server.py line 8570 ...", + "llm_provider": "openai", + }, + ) + data = LLMCallSpanData.from_standard_logging_payload(payload) + assert data.error is not None + assert data.error.error_type == "BadRequestError" + assert data.error.message == "400 violated moderation policy" + assert data.error.code == "400" + assert data.error.stack_trace == "File proxy_server.py line 8570 ..." + assert data.error.llm_provider == "openai" + + +def test_llm_call_adapter_error_details_default_to_none_when_absent(): + """Guardrail-shape payloads carry only ``error_class`` + ``error_message``. + The detail fields must stay ``None`` so the emitter's ``if error.code:`` + guards skip stamping empty attributes.""" + payload = _sample_payload( + status="failure", + error_information={ + "error_class": "ContentFilter", + "error_message": "guardrail rejected", + }, + ) + data = LLMCallSpanData.from_standard_logging_payload(payload) + assert data.error is not None + assert data.error.code is None + assert data.error.stack_trace is None + assert data.error.llm_provider is None + + def test_adapter_is_resilient_to_minimal_payload(): data = LLMCallSpanData.from_standard_logging_payload({}) assert data.request_model == "" From e9e30dffb68264e497d846763853e4f1c96939e7 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Wed, 8 Jul 2026 13:47:53 -0700 Subject: [PATCH 020/399] refactor(ui): reskin shared DataTable from tremor onto shadcn table primitives (#32209) * test(ui): characterize DataTable behavior before shadcn reskin Pins the shared view_logs DataTable contract with library-agnostic queries ahead of the tremor-to-shadcn table migration: loading and empty states, TanStack column defs with custom cell renderers, onRowClick payload, both expansion render paths (colspan sub-component and sibling child rows), the getRowCanExpand gate, and client-side sorting on and off. These must pass unchanged after the reskin. * refactor(ui): reskin shared DataTable from tremor onto shadcn table primitives Swaps the view_logs DataTable's presentational layer from @tremor/react to the in-repo components/ui/table primitives and hardens the seam that every later table migration copies: - getRowId is injected instead of hardcoded to request_id through an any cast; identity defaults to the row index and the logs page now passes request_id explicitly, keeping expansion state attached to the right row across refetch reorders - one expansion render path: renderChildRows had zero consumers and is removed; renderSubComponent (colspan cell) is the single path - the four consumers passing dead no-op renderSubComponent and getRowCanExpand boilerplate drop it - loading and empty defaults become generic (Loading... / No results) instead of log-specific The characterization tests from the previous commit pass unchanged except the dead child-rows path test, replaced by a reorder-stability test for injected getRowId plus coverage of the new generic defaults. First tremor removal of the tables track; view_logs/table.tsx no longer imports @tremor/react. * test(ui): assert child rows hidden before expansion in DataTable test * fix(ui): suppress row hover on DataTable placeholder rows * feat(ui): polish DataTable with skeleton loading, header band, and numeric column alignment * feat(ui): shape DataTable skeletons per column and keep stale rows during refetch * revert(ui): drop DataTable skeleton loading, restore text loading row * fix(ui): clip DataTable to its rounded wrapper and right-align Duration/TTFT values --- ui/litellm-dashboard/eslint-metrics.json | 2 +- ui/litellm-dashboard/eslint-suppressions.json | 5 -- .../components/EntityUsage/TopKeyView.tsx | 9 +-- .../components/EntityUsage/TopModelView.tsx | 12 ++- .../components/mcp_tools/MCPToolsetsTab.tsx | 2 - .../src/components/pass_through_settings.tsx | 2 - .../src/components/view_logs/columns.tsx | 10 ++- .../src/components/view_logs/index.tsx | 1 + .../src/components/view_logs/table.test.tsx | 81 ++++++++++++++++--- .../src/components/view_logs/table.tsx | 78 +++++++++--------- 10 files changed, 124 insertions(+), 78 deletions(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index 219cb0580e7..51cef1169f9 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,5 +1,5 @@ { - "@typescript-eslint/no-explicit-any": 1990, + "@typescript-eslint/no-explicit-any": 1988, "complexity": 128, "max-depth": 59, "no-console": 15 diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 1c8f92b720f..67b19471aaf 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -2077,11 +2077,6 @@ "count": 1 } }, - "src/components/view_logs/table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/view_user_spend.tsx": { "react-hooks/set-state-in-effect": { "count": 2 diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx index d0748583c30..40bc41b3e8c 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx @@ -164,6 +164,7 @@ const TopKeyView: React.FC = ({ topKeys, teams, showTags = fals const spendColumn = { header: "Spend (USD)", accessorKey: "spend", + meta: { numeric: true }, cell: (info: any) => { const value = info.getValue(); return value > 0 && value < 0.01 ? "<$0.01" : `$${formatNumberWithCommas(value, 2)}`; @@ -247,13 +248,7 @@ const TopKeyView: React.FC = ({ topKeys, teams, showTags = fals
) : (
- <>} - getRowCanExpand={() => false} - isLoading={false} - /> +
)} diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx index c69ba42f182..7562ef06a03 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx @@ -30,6 +30,7 @@ export default function TopModelView({ topModels, topModelsLimit, setTopModelsLi { header: "Spend (USD)", accessorKey: "spend", + meta: { numeric: true }, cell: (info: any) => { const value = info.getValue(); return `$${formatNumberWithCommas(value, 2)}`; @@ -38,16 +39,19 @@ export default function TopModelView({ topModels, topModelsLimit, setTopModelsLi { header: "Successful", accessorKey: "successful_requests", + meta: { numeric: true }, cell: (info: any) => {info.getValue()?.toLocaleString() || 0}, }, { header: "Failed", accessorKey: "failed_requests", + meta: { numeric: true }, cell: (info: any) => {info.getValue()?.toLocaleString() || 0}, }, { header: "Tokens", accessorKey: "tokens", + meta: { numeric: true }, cell: (info: any) => info.getValue()?.toLocaleString() || 0, }, ]; @@ -99,13 +103,7 @@ export default function TopModelView({ topModels, topModelsLimit, setTopModelsLi
) : (
- <>} - getRowCanExpand={() => false} - isLoading={false} - /> +
)} diff --git a/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx b/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx index 0198b830229..546df9ebc4b 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx @@ -509,8 +509,6 @@ export function MCPToolsetsTab({ accessToken, userRole }: MCPToolsetsTabProps) {
} - getRowCanExpand={() => false} isLoading={isLoading} noDataMessage="No toolsets yet. Click 'New Toolset' to create one." loadingMessage="Loading toolsets..." diff --git a/ui/litellm-dashboard/src/components/pass_through_settings.tsx b/ui/litellm-dashboard/src/components/pass_through_settings.tsx index 0fdd9c632bf..63fe0f92961 100644 --- a/ui/litellm-dashboard/src/components/pass_through_settings.tsx +++ b/ui/litellm-dashboard/src/components/pass_through_settings.tsx @@ -263,8 +263,6 @@ const PassThroughSettings: React.FC = ({
} - getRowCanExpand={() => false} isLoading={false} noDataMessage="No pass-through endpoints configured" /> diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 0508c562df8..7452992ed59 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -231,13 +231,14 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] : "Cost", accessorKey: "spend", size: 110, + meta: { numeric: true }, cell: (info: any) => { const row = info.row.original; const mcpCount = row.mcp_tool_call_count || 0; const mcpSpend = row.mcp_tool_call_spend || 0; return ( -
+
{getSpendString(info.getValue() || 0)} @@ -263,13 +264,14 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Duration (s)", accessorKey: "request_duration_ms", + meta: { numeric: true }, cell: (info: any) => { const ms = info.getValue(); if (ms == null) return -; const seconds = (ms / 1000).toFixed(2); return ( - {seconds} + {seconds} ); }, @@ -287,6 +289,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "TTFT (s)", accessorKey: "completionStartTime", + meta: { numeric: true }, cell: (info: any) => { const row = info.row.original; const completionStartTime = info.getValue(); @@ -298,7 +301,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] const ttftSeconds = (ttftMs / 1000).toFixed(2); return ( - {ttftSeconds} + {ttftSeconds} ); }, @@ -395,6 +398,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] : "Tokens", accessorKey: "total_tokens", size: 140, + meta: { numeric: true }, cell: (info: any) => { const row = info.row.original; return ( diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index 265e331e6a9..ee08712e56b 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -287,6 +287,7 @@ export default function SpendLogsTable({ accessToken, token, userRole, userID, p row.request_id} onRowClick={handleRowClick} isLoading={isLogsLoading} /> diff --git a/ui/litellm-dashboard/src/components/view_logs/table.test.tsx b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx index 7299d280769..9a8469cfeba 100644 --- a/ui/litellm-dashboard/src/components/view_logs/table.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx @@ -74,6 +74,35 @@ describe("DataTable states", () => { expect(screen.getByText("Nothing here")).toBeInTheDocument(); }); + it("falls back to generic loading and empty defaults", () => { + const { rerender } = render(); + expect(screen.getByText("Loading...")).toBeInTheDocument(); + + rerender(); + expect(screen.getByText("No results")).toBeInTheDocument(); + }); + + it("suppresses the primitive's row hover on loading, empty, and expansion placeholder rows", async () => { + const user = userEvent.setup(); + const { rerender } = render(); + expect(screen.getByText("Loading...").closest("tr")).toHaveClass("hover:bg-transparent"); + + rerender(); + expect(screen.getByText("No results").closest("tr")).toHaveClass("hover:bg-transparent"); + + rerender( + true} + renderSubComponent={({ row }) =>
details for {row.original.request_id}
} + />, + ); + await user.click(screen.getByRole("button", { name: "expand r1" })); + expect(screen.getByText("details for r1").closest("tr")).toHaveClass("hover:bg-transparent"); + expect(screen.getByText("alpha").closest("tr")).not.toHaveClass("hover:bg-transparent"); + }); + it("renders row data through plain TanStack column defs, including custom cell renderers", () => { const columns: ColumnDef[] = [ { header: "A", accessorKey: "a" }, @@ -84,6 +113,29 @@ describe("DataTable states", () => { expect(screen.getByText("alpha")).toBeInTheDocument(); expect(screen.getByText("custom:beta")).toBeInTheDocument(); }); + + it("clips the table to the rounded wrapper so the header band cannot bleed past the corners", () => { + const { container } = render(); + + const wrapper = container.firstElementChild; + expect(wrapper).toHaveClass("rounded-lg", "overflow-hidden"); + }); + + it("right-aligns headers and cells with tabular figures for numeric meta columns", () => { + const columns: ColumnDef[] = [ + { header: "A", accessorKey: "a" }, + { header: "B", accessorKey: "b", meta: { numeric: true } }, + ]; + render(); + + const headers = screen.getAllByRole("columnheader"); + expect(headers[1].querySelector("div")).toHaveClass("justify-end"); + expect(headers[0].querySelector("div")).not.toHaveClass("justify-end"); + + const cells = screen.getAllByRole("cell"); + expect(cells[1]).toHaveClass("text-right", "tabular-nums"); + expect(cells[0]).not.toHaveClass("text-right"); + }); }); describe("DataTable row interaction", () => { @@ -129,28 +181,33 @@ describe("DataTable expansion", () => { expect(screen.queryByText("details for r1")).not.toBeInTheDocument(); }); - it("renders child rows as sibling table rows (child-rows path)", async () => { + it("keeps expansion attached to the same row through data reorders when getRowId is injected", async () => { const user = userEvent.setup(); - render( + const { rerender } = render( row.request_id} getRowCanExpand={() => true} - renderChildRows={({ row }) => ( - - child of {row.original.request_id} - - )} + renderSubComponent={({ row }) =>
details for {row.original.request_id}
} />, ); - expect(screen.queryByText("child of r2")).not.toBeInTheDocument(); + await user.click(screen.getByRole("button", { name: "expand r1" })); + expect(screen.getByText("details for r1")).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: "expand r2" })); + rerender( + row.request_id} + getRowCanExpand={() => true} + renderSubComponent={({ row }) =>
details for {row.original.request_id}
} + />, + ); - const childCell = screen.getByText("child of r2"); - expect(childCell.closest("tr")).not.toBeNull(); - expect(within(screen.getByRole("table")).getByText("child of r2")).toBeInTheDocument(); + expect(screen.getByText("details for r1")).toBeInTheDocument(); + expect(screen.queryByText("details for r2")).not.toBeInTheDocument(); }); it("does not expand rows when getRowCanExpand is missing even if a renderer is provided", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/table.tsx b/ui/litellm-dashboard/src/components/view_logs/table.tsx index 4510cc9a1f0..c96f34f9b93 100644 --- a/ui/litellm-dashboard/src/components/view_logs/table.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/table.tsx @@ -1,6 +1,7 @@ import { Fragment, useState } from "react"; import { ColumnDef, + RowData, flexRender, getCoreRowModel, getExpandedRowModel, @@ -10,16 +11,21 @@ import { SortingState, } from "@tanstack/react-table"; -import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react"; +import { Table, TableHeader, TableHead, TableBody, TableRow, TableCell } from "@/components/ui/table"; + +declare module "@tanstack/react-table" { + interface ColumnMeta { + numeric?: boolean; + } +} interface DataTableProps { data: TData[]; columns: ColumnDef[]; + getRowId?: (row: TData, index: number) => string; onRowClick?: (row: TData) => void; - /** Renders inside a single colspan cell (used by audit logs) */ + /** Renders inside a single colspan cell */ renderSubComponent?: (props: { row: Row }) => React.ReactElement; - /** Renders directly in tbody as sibling table rows (used by MCP children) */ - renderChildRows?: (props: { row: Row }) => React.ReactNode; getRowCanExpand?: (row: Row) => boolean; isLoading?: boolean; loadingMessage?: string; @@ -31,16 +37,16 @@ interface DataTableProps { export function DataTable({ data = [], columns, + getRowId, onRowClick, renderSubComponent, - renderChildRows, getRowCanExpand, isLoading = false, - loadingMessage = "🚅 Loading logs...", - noDataMessage = "No logs found", + loadingMessage = "Loading...", + noDataMessage = "No results", enableSorting = false, }: DataTableProps) { - const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand; + const supportsExpansion = !!renderSubComponent && !!getRowCanExpand; const hasExplicitColumnSizes = columns.some((column) => column.size !== undefined); const [sorting, setSorting] = useState([]); @@ -55,58 +61,56 @@ export function DataTable({ enableSortingRemoval: false, }), ...(supportsExpansion && { getRowCanExpand }), - getRowId: (row: TData, index: number) => { - const _row: any = row as any; - return _row?.request_id ?? String(index); - }, + ...(getRowId && { getRowId }), getCoreRowModel: getCoreRowModel(), ...(enableSorting && { getSortedRowModel: getSortedRowModel() }), ...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }), }); - const tableClassName = hasExplicitColumnSizes - ? "[&_td]:py-0.5 [&_th]:py-1 [&_table]:table-fixed" - : "[&_td]:py-0.5 [&_th]:py-1 table-fixed w-full box-border"; + const tableClassName = hasExplicitColumnSizes ? "table-fixed" : "table-fixed w-full box-border"; const tableStyle = hasExplicitColumnSizes ? { minWidth: table.getCenterTotalSize() } : { minWidth: "400px" }; return ( -
+
- + {table.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => { const canSort = enableSorting && header.column.getCanSort(); const isSorted = header.column.getIsSorted(); + const numeric = header.column.columnDef.meta?.numeric; return ( - {header.isPlaceholder ? null : ( -
+
{flexRender(header.column.columnDef.header, header.getContext())} {canSort && ( - + {isSorted === "asc" ? "↑" : isSorted === "desc" ? "↓" : "⇅"} )}
)} - + ); })} ))} - + {isLoading ? ( - + -
+

{loadingMessage}

@@ -115,13 +119,15 @@ export function DataTable({ table.getRowModel().rows.map((row) => ( onRowClick?.(row.original)} > {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} @@ -129,12 +135,8 @@ export function DataTable({ ))} - {/* Child rows rendered as real table rows (MCP children) */} - {supportsExpansion && row.getIsExpanded() && renderChildRows && renderChildRows({ row })} - - {/* Legacy sub-component in colspan cell (audit logs) */} - {supportsExpansion && row.getIsExpanded() && renderSubComponent && !renderChildRows && ( - + {supportsExpansion && row.getIsExpanded() && renderSubComponent && ( +
{renderSubComponent({ row })}
@@ -143,11 +145,9 @@ export function DataTable({
)) ) : ( - - -
-

{noDataMessage}

-
+ + +

{noDataMessage}

)} From 0f1e29b33486ba6e1600fb93de7214e57e54047d Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:00:24 -0700 Subject: [PATCH 021/399] fix(bedrock): preserve cache_control ttl on message-level cache points (#32538) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com> --- .../prompt_templates/factory.py | 12 ++- ...llm_core_utils_prompt_templates_factory.py | 81 +++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index c1635158d3b..06abb591717 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -4377,6 +4377,7 @@ class BedrockConverseMessagesProcessor: _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: _parts.append(_cache_point_block) @@ -4384,7 +4385,7 @@ class BedrockConverseMessagesProcessor: elif message_block["content"] and isinstance(message_block["content"], str): _part = BedrockContentBlock(text=messages[msg_i]["content"]) _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - message_block, block_type="content_block" + message_block, block_type="content_block", model=model ) user_content.append(_part) if _cache_point_block is not None: @@ -4509,6 +4510,7 @@ class BedrockConverseMessagesProcessor: _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: assistants_parts.append(_cache_point_block) @@ -4520,7 +4522,7 @@ class BedrockConverseMessagesProcessor: # If content is empty/whitespace, skip it (don't add a placeholder) # Add cache point block for assistant string content _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - assistant_message_block, block_type="content_block" + assistant_message_block, block_type="content_block", model=model ) if _cache_point_block is not None: assistant_content.append(_cache_point_block) @@ -4745,6 +4747,7 @@ def _bedrock_converse_messages_pt( _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: _parts.append(_cache_point_block) @@ -4752,7 +4755,7 @@ def _bedrock_converse_messages_pt( elif message_block["content"] and isinstance(message_block["content"], str): _part = BedrockContentBlock(text=messages[msg_i]["content"]) _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - message_block, block_type="content_block" + message_block, block_type="content_block", model=model ) user_content.append(_part) if _cache_point_block is not None: @@ -4882,6 +4885,7 @@ def _bedrock_converse_messages_pt( _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: assistants_parts.append(_cache_point_block) @@ -4892,7 +4896,7 @@ def _bedrock_converse_messages_pt( assistant_content.append(BedrockContentBlock(text=_assistant_content)) # Add cache point block for assistant string content _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - assistant_message_block, block_type="content_block" + assistant_message_block, block_type="content_block", model=model ) if _cache_point_block is not None: assistant_content.append(_cache_point_block) diff --git a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py index 1d5289737f1..bcda88ea609 100644 --- a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py +++ b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py @@ -3085,3 +3085,84 @@ def test_bedrock_converse_messages_pt_document_rejects_url_source(): _bedrock_converse_messages_pt( messages, "anthropic.claude-sonnet-4-6", "bedrock" ) + + +def _collect_cache_points(blocks): + return [ + block["cachePoint"] + for message in blocks + for block in message["content"] + if "cachePoint" in block + ] + + +@pytest.mark.parametrize( + "messages", + [ + [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "conversation history", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ], + }, + ], + [ + {"role": "user", "content": "hello"}, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "assistant reply", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ], + }, + ], + ], +) +def test_bedrock_converse_message_level_cache_point_preserves_ttl(messages): + """ + Regression for https://github.com/BerriAI/litellm/issues/32154: message-level + cache_control ttl was silently dropped because the message-level + _get_cache_point_block call sites never passed model=, so multi-turn prefixes + fell back to the 5m default while the system prompt kept 1h, churning the + cache every turn on models like Opus 4.8. + """ + result = _bedrock_converse_messages_pt( + messages=messages, + model="eu.anthropic.claude-opus-4-8", + llm_provider="bedrock", + ) + + cache_points = _collect_cache_points(result) + assert cache_points == [{"type": "default", "ttl": "1h"}] + + +@pytest.mark.asyncio +async def test_bedrock_converse_message_level_cache_point_preserves_ttl_async(): + messages = [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "conversation history", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ], + }, + ] + + result = await BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async( + messages=messages, + model="eu.anthropic.claude-opus-4-8", + llm_provider="bedrock", + ) + + assert _collect_cache_points(result) == [{"type": "default", "ttl": "1h"}] From 7b2742777d31d2c7af6eeb5e1d3a3770d3570c81 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 14:07:29 -0700 Subject: [PATCH 022/399] refactor(ui): dedupe /health/license fetch via shared useLicenseInfo hook UsageIndicator was fetching /health/license through its own useEffect while the new expiry banner fetches the same endpoint via useLicenseInfo, so an admin with the usage widget open made two identical calls per page load. Point UsageIndicator at useLicenseInfo too; both callers now share one React Query cache entry, collapsing it back to a single request. The null/error semantics are preserved (data ?? null matches the previous catch-to-null), and license errors never fed the widget's error state before either --- .../src/components/UsageIndicator.test.tsx | 30 +++++++++++-------- .../src/components/UsageIndicator.tsx | 12 ++++---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx b/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx index 8c7c15bc5a5..ad27fbcd74f 100644 --- a/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx +++ b/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx @@ -2,6 +2,7 @@ import React from "react"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import UsageIndicator from "./UsageIndicator"; vi.mock("./networking", () => ({ @@ -17,6 +18,11 @@ import { getRemainingUsers } from "./networking"; const mockGetRemainingUsers = vi.mocked(getRemainingUsers); +const renderWithClient = (ui: React.ReactElement) => { + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + return render({ui}); +}; + const DEFAULT_USAGE_DATA = { total_users: 100, total_users_used: 1, @@ -33,7 +39,7 @@ describe("UsageIndicator", () => { }); it("should render when given access token and usage data loads", async () => { - render(); + renderWithClient(); await screen.findByText("Usage"); @@ -41,7 +47,7 @@ describe("UsageIndicator", () => { }); it("should not show Near limit when users usage is below 80% (1/100 -> 1%)", async () => { - render(); + renderWithClient(); await screen.findByText("Usage"); @@ -58,7 +64,7 @@ describe("UsageIndicator", () => { total_users_remaining: null, }); - render(); + renderWithClient(); await waitFor(() => { expect(screen.queryByText("Usage")).not.toBeInTheDocument(); @@ -76,7 +82,7 @@ describe("UsageIndicator", () => { total_teams_remaining: 1, }); - render(); + renderWithClient(); await screen.findByText("Usage"); @@ -94,7 +100,7 @@ describe("UsageIndicator", () => { total_teams_remaining: null, }); - render(); + renderWithClient(); await screen.findByText("Usage"); @@ -112,7 +118,7 @@ describe("UsageIndicator", () => { total_teams_remaining: -2, }); - render(); + renderWithClient(); await screen.findByText("Usage"); @@ -121,7 +127,7 @@ describe("UsageIndicator", () => { }); it("should render nothing when accessToken is null", () => { - render(); + renderWithClient(); expect(mockGetRemainingUsers).not.toHaveBeenCalled(); expect(screen.queryByText("Usage")).not.toBeInTheDocument(); @@ -131,7 +137,7 @@ describe("UsageIndicator", () => { const { useDisableUsageIndicator } = await import("@/app/(dashboard)/hooks/useDisableUsageIndicator"); (useDisableUsageIndicator as ReturnType).mockReturnValue(true); - render(); + renderWithClient(); await waitFor(() => { expect(screen.queryByText("Usage")).not.toBeInTheDocument(); @@ -143,7 +149,7 @@ describe("UsageIndicator", () => { it("should show Loading while fetching", () => { mockGetRemainingUsers.mockImplementation(() => new Promise(() => {})); - render(); + renderWithClient(); expect(screen.getByText("Loading...")).toBeInTheDocument(); }); @@ -152,7 +158,7 @@ describe("UsageIndicator", () => { const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); mockGetRemainingUsers.mockRejectedValue(new Error("Network error")); - render(); + renderWithClient(); expect(await screen.findByText("Failed to load usage data")).toBeInTheDocument(); @@ -161,7 +167,7 @@ describe("UsageIndicator", () => { it("should minimize when user clicks minimize button", async () => { const user = userEvent.setup(); - render(); + renderWithClient(); await screen.findByText("Usage"); @@ -174,7 +180,7 @@ describe("UsageIndicator", () => { it("should restore from minimized when user clicks restore button", async () => { const user = userEvent.setup(); - render(); + renderWithClient(); await screen.findByText("Usage"); diff --git a/ui/litellm-dashboard/src/components/UsageIndicator.tsx b/ui/litellm-dashboard/src/components/UsageIndicator.tsx index 8165f8eb6f6..6e7b5e9ec60 100644 --- a/ui/litellm-dashboard/src/components/UsageIndicator.tsx +++ b/ui/litellm-dashboard/src/components/UsageIndicator.tsx @@ -12,10 +12,11 @@ import { Users, } from "lucide-react"; import { useEffect, useState } from "react"; -import { getRemainingUsers, getLicenseInfo, LicenseInfo } from "./networking"; +import { getRemainingUsers } from "./networking"; import { cn } from "@/lib/cva.config"; import { getDaysUntilExpiration } from "@/utils/licenseUtils"; +import { useLicenseInfo } from "@/app/(dashboard)/hooks/license/useLicenseInfo"; interface UsageIndicatorProps { accessToken: string | null; @@ -48,10 +49,11 @@ export default function UsageIndicator({ accessToken, width = 220 }: UsageIndica const [isExpanded, setIsExpanded] = useState(false); const [isMinimized, setIsMinimized] = useState(false); const [data, setData] = useState(null); - const [licenseInfo, setLicenseInfo] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); + const licenseInfo = useLicenseInfo(accessToken).data ?? null; + useEffect(() => { const fetchData = async () => { if (!accessToken) return; @@ -60,12 +62,8 @@ export default function UsageIndicator({ accessToken, width = 220 }: UsageIndica setError(null); try { - const [usageResult, licenseResult] = await Promise.all([ - getRemainingUsers(accessToken), - getLicenseInfo(accessToken).catch(() => null), // Don't fail if license endpoint unavailable - ]); + const usageResult = await getRemainingUsers(accessToken); setData(usageResult); - setLicenseInfo(licenseResult); } catch (err) { console.error("Failed to fetch usage data:", err); setError("Failed to load usage data"); From 5973d9fd2b0d074f963e95fdae2b9c1aef3d88bc Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Wed, 8 Jul 2026 14:32:16 -0700 Subject: [PATCH 023/399] feat(ui): add eslint rules for nested ternaries, large inline object args, and long condition chains (#32415) * feat(ui): add eslint rules for nested ternaries, large inline object args, and long condition chains Adds three dashboard lint rules to keep new code readable. Nested ternaries are banned outright via the built-in no-nested-ternary, with the 265 existing occurrences grandfathered in eslint-suppressions.json so only new ones fail. Two custom rules ship as a small local plugin under scripts/eslint-rules: no-large-inline-object-arg flags object literals with 4+ properties passed straight into a call, nudging toward a named variable, and no-long-condition-chain flags boolean expressions that combine 4+ conditions, nudging toward a named boolean. Both are warnings tracked on the existing budget ratchet (eslint-budgets.json + eslint-metrics.json) with headroom above the current counts, so they ratchet down over time rather than freezing a baseline. Both thresholds are configurable rule options and covered by RuleTester unit tests. * fix(ui): scope no-long-condition-chain to boolean operators, not nullish Greptile flagged that the rule counted nullish-coalescing chains the same as &&/|| chains, so a 4-part `a ?? b ?? c ?? d` fallback surfaced "Boolean expression combines 4 conditions", which is inaccurate since a `??` fallback is value defaulting, not a condition. Restrict the visitor to && / || nodes so `??` chains are treated as leaves, while a boolean chain nested inside a `??` is still caught. Drops 6 miscounted occurrences (240 -> 234). * chore(ui): sync lint metrics and suppressions with staging Merge advanced the base branch, adding one no-large-inline-object-arg occurrence (508 -> 509) and making one grandfathered react-hooks suppression stale. Regenerate eslint-metrics.json and prune the suppression so the budget/drift gate passes. * chore(ui): sync lint metrics with staging Merge advanced the base, adding four no-large-inline-object-arg occurrences (509 -> 513). Regenerate eslint-metrics.json so the drift gate passes. --- ui/litellm-dashboard/eslint-budgets.json | 4 +- ui/litellm-dashboard/eslint-metrics.json | 2 + ui/litellm-dashboard/eslint-suppressions.json | 442 +++++++++++++++++- ui/litellm-dashboard/eslint.config.mjs | 6 +- .../scripts/eslint-rules/index.mjs | 11 + .../no-large-inline-object-arg.mjs | 41 ++ .../eslint-rules/no-long-condition-chain.mjs | 41 ++ .../no-large-inline-object-arg.test.ts | 46 ++ .../no-long-condition-chain.test.ts | 51 ++ 9 files changed, 641 insertions(+), 3 deletions(-) create mode 100644 ui/litellm-dashboard/scripts/eslint-rules/index.mjs create mode 100644 ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs create mode 100644 ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs create mode 100644 ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts create mode 100644 ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts diff --git a/ui/litellm-dashboard/eslint-budgets.json b/ui/litellm-dashboard/eslint-budgets.json index 8dedb9ac9ca..f08e1bb6160 100644 --- a/ui/litellm-dashboard/eslint-budgets.json +++ b/ui/litellm-dashboard/eslint-budgets.json @@ -2,5 +2,7 @@ "@typescript-eslint/no-explicit-any": { "max": 2040, "target": 1500 }, "no-console": { "max": 484, "target": 0 }, "complexity": { "max": 140, "target": 80 }, - "max-depth": { "max": 70, "target": 30 } + "max-depth": { "max": 70, "target": 30 }, + "local/no-large-inline-object-arg": { "max": 560, "target": 300 }, + "local/no-long-condition-chain": { "max": 265, "target": 120 } } diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index 51cef1169f9..f4dc89c5b80 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,6 +1,8 @@ { "@typescript-eslint/no-explicit-any": 1988, "complexity": 128, + "local/no-large-inline-object-arg": 513, + "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 } diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 67b19471aaf..b077338c75b 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1,4 +1,9 @@ { + "scripts/check-lint-budgets.mjs": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/(dashboard)/api-reference/APIReferenceView.tsx": { "no-restricted-imports": { "count": 1 @@ -64,6 +69,9 @@ } }, "src/app/(dashboard)/cost-tracking/components/cost_tracking_settings.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -133,11 +141,21 @@ "count": 1 } }, + "src/app/(dashboard)/guardrails-monitor/components/GuardrailDetail.tsx": { + "no-nested-ternary": { + "count": 3 + } + }, "src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx": { "no-restricted-imports": { "count": 1 } }, + "src/app/(dashboard)/guardrails-monitor/components/GuardrailsOverview.tsx": { + "no-nested-ternary": { + "count": 8 + } + }, "src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx": { "react/display-name": { "count": 1 @@ -343,6 +361,9 @@ } }, "src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -361,6 +382,9 @@ } }, "src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 5 } @@ -370,7 +394,15 @@ "count": 1 } }, + "src/app/(dashboard)/playground/components/chat_ui/ChatMessageBubble.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx": { + "no-nested-ternary": { + "count": 7 + }, "no-restricted-imports": { "count": 1 }, @@ -382,6 +414,9 @@ } }, "src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-syntax": { "count": 2 } @@ -392,6 +427,9 @@ } }, "src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/immutability": { "count": 2 }, @@ -400,16 +438,27 @@ } }, "src/app/(dashboard)/playground/components/compareUI/CompareUI.tsx": { + "no-nested-ternary": { + "count": 4 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, + "src/app/(dashboard)/playground/components/compareUI/components/MessageDisplay.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/(dashboard)/playground/components/compareUI/components/ModelSelector.tsx": { "no-restricted-imports": { "count": 1 } }, "src/app/(dashboard)/playground/components/complianceUI/ComplianceUI.tsx": { + "no-nested-ternary": { + "count": 8 + }, "react-hooks/preserve-manual-memoization": { "count": 3 } @@ -474,6 +523,9 @@ } }, "src/app/(dashboard)/projects/components/ProjectDetailsPage.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -499,6 +551,9 @@ } }, "src/app/(dashboard)/prompts/components/index.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -517,6 +572,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_editor_view/PromptCodeSnippets.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -550,6 +608,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_editor_view/VersionHistorySidePanel.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/immutability": { "count": 1 } @@ -570,6 +631,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_info.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -578,6 +642,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -635,6 +702,9 @@ } }, "src/app/(dashboard)/users/_components/user_edit_view.test.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react/display-name": { "count": 1 } @@ -648,6 +718,9 @@ } }, "src/app/(dashboard)/users/_components/view_users.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -664,6 +737,9 @@ } }, "src/app/(dashboard)/users/_components/view_users/table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -677,6 +753,9 @@ } }, "src/app/(dashboard)/workflows/WorkflowRuns.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-syntax": { "count": 3 }, @@ -684,6 +763,11 @@ "count": 1 } }, + "src/app/chat/page.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/login/LoginPage.tsx": { "react-hooks/set-state-in-effect": { "count": 2 @@ -715,6 +799,9 @@ } }, "src/components/AIHub/ModelHubTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -746,6 +833,9 @@ } }, "src/components/AIHub/forms/MakeMCPPublicForm.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 }, @@ -778,11 +868,17 @@ } }, "src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -815,11 +911,26 @@ "count": 1 } }, + "src/components/GuardrailSettingsView.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/GuardrailsMonitor/LogViewer.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/HelpLink.test.tsx": { "unused-imports/no-unused-imports": { "count": 1 } }, + "src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Navbar/BlogDropdown/BlogDropdown.test.tsx": { "max-nested-callbacks": { "count": 12 @@ -836,6 +947,9 @@ } }, "src/components/OldTeams.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 }, @@ -856,7 +970,15 @@ "count": 1 } }, + "src/components/Settings/AdminSettings/HashicorpVault/HashicorpVault.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -871,6 +993,11 @@ "count": 1 } }, + "src/components/Settings/AdminSettings/SSOSettings/RedactableField.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx": { "max-nested-callbacks": { "count": 4 @@ -881,7 +1008,15 @@ "count": 2 } }, + "src/components/Settings/AdminSettings/UISettings/UISettings.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -907,12 +1042,20 @@ "count": 1 } }, + "src/components/TeamSSOSettings.test.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/ToolDetail.tsx": { "unused-imports/no-unused-imports": { "count": 2 } }, "src/components/ToolPolicies.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -932,6 +1075,9 @@ } }, "src/components/UsageIndicator.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 }, @@ -949,6 +1095,11 @@ "count": 1 } }, + "src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/UsagePage/components/EntityUsage/EntityUsage.tsx": { "no-restricted-imports": { "count": 1 @@ -975,11 +1126,17 @@ } }, "src/components/UsagePage/components/UsageAIChatPanel.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/immutability": { "count": 1 } }, "src/components/UsagePage/components/UsagePageView.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -999,16 +1156,25 @@ } }, "src/components/VirtualKeysPage/VirtualKeysTable.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } }, "src/components/activity_metrics.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/add_model/AddModelForm.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1045,11 +1211,22 @@ } }, "src/components/add_model/litellm_model_name.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, + "src/components/add_model/model_connection_test.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/add_model/provider_specific_fields.tsx": { + "no-nested-ternary": { + "count": 5 + }, "no-restricted-imports": { "count": 1 }, @@ -1079,6 +1256,9 @@ } }, "src/components/agents/add_agent_form.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1102,7 +1282,15 @@ "count": 1 } }, + "src/components/agents/agent_form_fields.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/agents/agent_info.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1110,7 +1298,20 @@ "count": 1 } }, + "src/components/agents/agent_virtual_keys.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/agents/dynamic_agent_form_fields.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/alerting/dynamic_form.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 } @@ -1123,6 +1324,31 @@ "count": 1 } }, + "src/components/chat/KeysPanel.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/MCPAppsPanel.tsx": { + "no-nested-ternary": { + "count": 7 + } + }, + "src/components/chat/MCPConnectPicker.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/MCPCredentialsTab.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/UsagePanel.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/claude_code_plugins.tsx": { "no-restricted-imports": { "count": 1 @@ -1145,6 +1371,9 @@ } }, "src/components/claude_code_plugins/plugin_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1235,6 +1464,9 @@ } }, "src/components/common_components/chartUtils.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1250,6 +1482,9 @@ } }, "src/components/common_components/simple_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1286,6 +1521,9 @@ } }, "src/components/general_settings.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 2 } @@ -1300,17 +1538,28 @@ "count": 1 } }, + "src/components/guardrails/GuardrailTestPlayground.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/guardrails/GuardrailTestResults.tsx": { "no-restricted-imports": { "count": 1 } }, "src/components/guardrails/TeamGuardrailsTab.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/add_guardrail_form.tsx": { + "no-nested-ternary": { + "count": 4 + }, "react-hooks/set-state-in-effect": { "count": 1 }, @@ -1319,11 +1568,17 @@ } }, "src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/content_filter/ContentCategoryConfiguration.tsx": { + "no-nested-ternary": { + "count": 3 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1342,6 +1597,9 @@ } }, "src/components/guardrails/custom_code/CustomCodeModal.tsx": { + "no-nested-ternary": { + "count": 6 + }, "no-restricted-imports": { "count": 1 }, @@ -1372,16 +1630,25 @@ } }, "src/components/guardrails/guardrail_optional_params.tsx": { + "no-nested-ternary": { + "count": 5 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/guardrail_provider_fields.tsx": { + "no-nested-ternary": { + "count": 5 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/guardrail_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 2 } @@ -1407,6 +1674,9 @@ "src/components/llm_calls/chat_completion.tsx": { "max-params": { "count": 1 + }, + "no-nested-ternary": { + "count": 1 } }, "src/components/llm_calls/responses_api.tsx": { @@ -1444,7 +1714,15 @@ "count": 1 } }, + "src/components/mcp_tools/MCPToolArgumentsForm.tsx": { + "no-nested-ternary": { + "count": 5 + } + }, "src/components/mcp_tools/MCPToolsetsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1456,11 +1734,17 @@ } }, "src/components/mcp_tools/McpCrudPermissionPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } }, "src/components/mcp_tools/OAuthFormFields.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1471,6 +1755,9 @@ } }, "src/components/mcp_tools/ToolTestPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1478,12 +1765,20 @@ "count": 1 } }, + "src/components/mcp_tools/UserEnvVarsModal.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/mcp_tools/create_mcp_server.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, "react-hooks/set-state-in-effect": { - "count": 5 + "count": 4 } }, "src/components/mcp_tools/mcp_connect.tsx": { @@ -1495,6 +1790,9 @@ } }, "src/components/mcp_tools/mcp_connection_status.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -1515,6 +1813,9 @@ } }, "src/components/mcp_tools/mcp_server_edit.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1531,6 +1832,9 @@ } }, "src/components/mcp_tools/mcp_servers.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1544,6 +1848,9 @@ } }, "src/components/mcp_tools/mcp_tools.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1575,6 +1882,9 @@ } }, "src/components/model_dashboard/HealthCheckComponent.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1583,6 +1893,9 @@ } }, "src/components/model_dashboard/all_models_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1591,11 +1904,17 @@ "max-params": { "count": 1 }, + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } }, "src/components/model_dashboard/table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1619,6 +1938,9 @@ } }, "src/components/model_info_view.tsx": { + "no-nested-ternary": { + "count": 14 + }, "no-restricted-imports": { "count": 1 }, @@ -1627,6 +1949,9 @@ } }, "src/components/molecules/filter.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/use-memo": { "count": 1 } @@ -1643,6 +1968,9 @@ "max-params": { "count": 1 }, + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1656,6 +1984,9 @@ "max-params": { "count": 23 }, + "no-nested-ternary": { + "count": 5 + }, "no-restricted-syntax": { "count": 154 } @@ -1731,6 +2062,9 @@ } }, "src/components/permissions/MCPServerPermissions.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -1740,6 +2074,11 @@ "count": 1 } }, + "src/components/policies/PolicySelector.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/policies/add_attachment_form.tsx": { "no-restricted-imports": { "count": 1 @@ -1760,6 +2099,9 @@ } }, "src/components/policies/ai_suggestion_modal.tsx": { + "no-nested-ternary": { + "count": 10 + }, "no-restricted-imports": { "count": 1 }, @@ -1773,11 +2115,17 @@ } }, "src/components/policies/attachment_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/policies/guardrail_selection_modal.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1788,6 +2136,9 @@ } }, "src/components/policies/impact_popover.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1806,6 +2157,9 @@ } }, "src/components/policies/pipeline_flow_builder.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1827,6 +2181,9 @@ } }, "src/components/policies/policy_table.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1856,6 +2213,9 @@ } }, "src/components/public_model_hub.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1865,12 +2225,25 @@ "count": 1 } }, + "src/components/router_settings/ReliabilityRetriesSection.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/routing_groups/index.tsx": { "react-hooks/preserve-manual-memoization": { "count": 1 } }, + "src/components/search_tools/SearchToolSelector.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/settings.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1925,6 +2298,9 @@ } }, "src/components/team/EditMembership.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1935,6 +2311,9 @@ } }, "src/components/team/TeamInfo.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1943,6 +2322,9 @@ } }, "src/components/team/TeamVirtualKeysTable.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1966,6 +2348,9 @@ } }, "src/components/templates/key_edit_view.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1976,6 +2361,9 @@ } }, "src/components/templates/key_info_view.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -2016,6 +2404,9 @@ } }, "src/components/vector_store_management/VectorStoreForm.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 }, @@ -2044,12 +2435,38 @@ "count": 1 } }, + "src/components/view_logs/EvalViewer/EvalViewer.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/view_logs/GuardrailViewer/CompliancePanel.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, + "src/components/view_logs/GuardrailViewer/ContentFilterDetails.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/view_logs/GuardrailViewer/GuardrailViewer.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, + "src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, "src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx": { + "no-nested-ternary": { + "count": 3 + }, "react-hooks/set-state-in-effect": { "count": 2 } @@ -2059,11 +2476,21 @@ "count": 2 } }, + "src/components/view_logs/LogDetailsDrawer/prettyMessagesUtils.ts": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/view_logs/LogDetailsDrawer/useKeyboardNavigation.ts": { "react-hooks/immutability": { "count": 2 } }, + "src/components/view_logs/LogsTableToolbar.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, "src/components/view_logs/columns.tsx": { "no-restricted-imports": { "count": 1 @@ -2077,6 +2504,11 @@ "count": 1 } }, + "src/components/view_logs/table.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/view_user_spend.tsx": { "react-hooks/set-state-in-effect": { "count": 2 @@ -2113,6 +2545,9 @@ } }, "src/hooks/useTestMCPConnection.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -2130,6 +2565,11 @@ "count": 1 } }, + "src/lib/http/client.ts": { + "no-nested-ternary": { + "count": 1 + } + }, "src/utils/dataUtils.test.ts": { "max-nested-callbacks": { "count": 1 diff --git a/ui/litellm-dashboard/eslint.config.mjs b/ui/litellm-dashboard/eslint.config.mjs index acdd0c91309..0cf5b4ff655 100644 --- a/ui/litellm-dashboard/eslint.config.mjs +++ b/ui/litellm-dashboard/eslint.config.mjs @@ -3,6 +3,7 @@ import tseslint from "typescript-eslint"; import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; import prettier from "eslint-config-prettier/flat"; import unusedImports from "eslint-plugin-unused-imports"; +import local from "./scripts/eslint-rules/index.mjs"; const eslintConfig = [ { @@ -13,9 +14,11 @@ const eslintConfig = [ ...nextCoreWebVitals, prettier, { - plugins: { "unused-imports": unusedImports }, + plugins: { "unused-imports": unusedImports, local }, rules: { "unused-imports/no-unused-imports": "error", + "local/no-large-inline-object-arg": "warn", + "local/no-long-condition-chain": "warn", "@typescript-eslint/no-explicit-any": "warn", "no-console": ["warn", { allow: ["warn", "error"] }], "@typescript-eslint/no-unused-vars": "off", @@ -28,6 +31,7 @@ const eslintConfig = [ "no-useless-escape": "off", "no-self-assign": "error", "no-var": "error", + "no-nested-ternary": "error", "react/no-danger": "error", complexity: ["warn", 20], "max-depth": ["warn", 4], diff --git a/ui/litellm-dashboard/scripts/eslint-rules/index.mjs b/ui/litellm-dashboard/scripts/eslint-rules/index.mjs new file mode 100644 index 00000000000..150ba1d02e9 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/index.mjs @@ -0,0 +1,11 @@ +import noLargeInlineObjectArg from "./no-large-inline-object-arg.mjs"; +import noLongConditionChain from "./no-long-condition-chain.mjs"; + +const plugin = { + rules: { + "no-large-inline-object-arg": noLargeInlineObjectArg, + "no-long-condition-chain": noLongConditionChain, + }, +}; + +export default plugin; diff --git a/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs b/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs new file mode 100644 index 00000000000..5c5ae170e23 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs @@ -0,0 +1,41 @@ +const DEFAULT_MIN_PROPERTIES = 4; + +const isArgumentOf = (node) => { + const parent = node.parent; + if (parent == null) return false; + if (parent.type !== "CallExpression" && parent.type !== "NewExpression") return false; + return parent.arguments.includes(node); +}; + +const rule = { + meta: { + type: "suggestion", + docs: { + description: + "Disallow passing a large object literal inline as a call argument; assign it to a named variable first.", + }, + schema: [ + { + type: "object", + properties: { minProperties: { type: "integer", minimum: 1 } }, + additionalProperties: false, + }, + ], + messages: { + tooLarge: + "Object literal with {{count}} properties passed inline as an argument; assign it to a named variable first.", + }, + }, + create(context) { + const minProperties = context.options[0]?.minProperties ?? DEFAULT_MIN_PROPERTIES; + return { + ObjectExpression(node) { + if (!isArgumentOf(node)) return; + if (node.properties.length < minProperties) return; + context.report({ node, messageId: "tooLarge", data: { count: node.properties.length } }); + }, + }; + }, +}; + +export default rule; diff --git a/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs b/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs new file mode 100644 index 00000000000..638e57442e2 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs @@ -0,0 +1,41 @@ +const DEFAULT_MIN_CONDITIONS = 4; + +const isBooleanLogical = (node) => + node?.type === "LogicalExpression" && (node.operator === "&&" || node.operator === "||"); + +const countConditions = (node) => + isBooleanLogical(node) ? countConditions(node.left) + countConditions(node.right) : 1; + +const rule = { + meta: { + type: "suggestion", + docs: { + description: + "Disallow logical expressions that combine many conditions; extract the condition into a named boolean.", + }, + schema: [ + { + type: "object", + properties: { minConditions: { type: "integer", minimum: 2 } }, + additionalProperties: false, + }, + ], + messages: { + tooMany: "Boolean expression combines {{count}} conditions; extract it into a named variable.", + }, + }, + create(context) { + const minConditions = context.options[0]?.minConditions ?? DEFAULT_MIN_CONDITIONS; + return { + LogicalExpression(node) { + if (!isBooleanLogical(node)) return; + if (isBooleanLogical(node.parent)) return; + const count = countConditions(node); + if (count < minConditions) return; + context.report({ node, messageId: "tooMany", data: { count } }); + }, + }; + }, +}; + +export default rule; diff --git a/ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts b/ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts new file mode 100644 index 00000000000..dfe22ea8266 --- /dev/null +++ b/ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts @@ -0,0 +1,46 @@ +import { RuleTester } from "eslint"; +import rule from "../../scripts/eslint-rules/no-large-inline-object-arg.mjs"; + +const ruleTester = new RuleTester({ + languageOptions: { ecmaVersion: "latest", sourceType: "module" }, +}); + +ruleTester.run("no-large-inline-object-arg", rule as never, { + valid: [ + "foo({ a: 1, b: 2, c: 3 });", + "foo({});", + "const opts = { a: 1, b: 2, c: 3, d: 4 }; foo(opts);", + "const x = { a: 1, b: 2, c: 3, d: 4 };", + "function f() { return { a: 1, b: 2, c: 3, d: 4 }; }", + "const arr = [{ a: 1, b: 2, c: 3, d: 4 }];", + "foo(1, 2, { a: 1, b: 2 });", + { code: "foo({ a: 1, b: 2, c: 3, d: 4 });", options: [{ minProperties: 5 }] }, + ], + invalid: [ + { + code: "foo({ a: 1, b: 2, c: 3, d: 4 });", + errors: [{ messageId: "tooLarge", data: { count: 4 } }], + }, + { + code: "new Widget({ a: 1, b: 2, c: 3, d: 4, e: 5 });", + errors: [{ messageId: "tooLarge", data: { count: 5 } }], + }, + { + code: "foo(1, { a: 1, b: 2, c: 3, d: 4 });", + errors: [{ messageId: "tooLarge" }], + }, + { + code: "foo({ a: 1, ...rest, c: 3, d: 4 });", + errors: [{ messageId: "tooLarge", data: { count: 4 } }], + }, + { + code: "foo({ a: 1, b: 2, c: 3 });", + options: [{ minProperties: 3 }], + errors: [{ messageId: "tooLarge", data: { count: 3 } }], + }, + { + code: "outer({ a: 1, b: 2, c: 3, d: 4 }, inner({ e: 5, f: 6, g: 7, h: 8 }));", + errors: [{ messageId: "tooLarge" }, { messageId: "tooLarge" }], + }, + ], +}); diff --git a/ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts b/ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts new file mode 100644 index 00000000000..4a7fb677190 --- /dev/null +++ b/ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts @@ -0,0 +1,51 @@ +import { RuleTester } from "eslint"; +import rule from "../../scripts/eslint-rules/no-long-condition-chain.mjs"; + +const ruleTester = new RuleTester({ + languageOptions: { ecmaVersion: "latest", sourceType: "module" }, +}); + +ruleTester.run("no-long-condition-chain", rule as never, { + valid: [ + "const x = a && b && c;", + "const x = a || b || c;", + "const x = a && (b || c);", + "const x = a && b;", + "if (a || b || c) {}", + "const x = a ?? b ?? c;", + "const url = a ?? b ?? c ?? d;", + "const x = (a && b) ?? c ?? d;", + { code: "const x = a && b && c && d;", options: [{ minConditions: 5 }] }, + ], + invalid: [ + { + code: "const x = a && b && c && d;", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + { + code: "const x = a || b || c || d || e;", + errors: [{ messageId: "tooMany", data: { count: 5 } }], + }, + { + code: "const x = a && b || c && d;", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + { + code: "if (!a && !b && !c && !d) {}", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + { + code: "const x = a && (b || c);", + options: [{ minConditions: 3 }], + errors: [{ messageId: "tooMany", data: { count: 3 } }], + }, + { + code: "const x = (a && b && c && d) || (e && f && g && h);", + errors: [{ messageId: "tooMany", data: { count: 8 } }], + }, + { + code: "const x = (a && b && c && d) ?? fallback;", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + ], +}); From 34aedc40c6467e8a81dbd18e8df71d20cb6bcd96 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 14:50:27 -0700 Subject: [PATCH 024/399] test(ui): mock LicenseExpiryBanner in the dashboard layout test The layout test renders DashboardShell without a QueryClientProvider and mocks DebugWarningBanner to null for exactly that reason. The new LicenseExpiryBanner also uses a React Query hook, so it needs the same treatment; without it the test threw "No QueryClient set". Runtime is unaffected: the app mounts a QueryClientProvider above the layout (DebugWarningBanner already relies on it) --- ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx index af68d9f87e9..7573ddb5a0f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx @@ -21,6 +21,10 @@ vi.mock("@/components/DebugWarningBanner", () => ({ DebugWarningBanner: () => null, })); +vi.mock("@/components/LicenseExpiryBanner", () => ({ + LicenseExpiryBanner: () => null, +})); + vi.mock("@/contexts/ThemeContext", () => ({ ThemeProvider: ({ children }: { children: React.ReactNode }) => <>{children}, })); From 528fa380f5a271865af9f85228148131063bdf2b Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Wed, 8 Jul 2026 15:05:27 -0700 Subject: [PATCH 025/399] fix(guardrails): forward grayswan scan id header (#32544) * fix(guardrails): forward grayswan scan id header * test(guardrails): cover grayswan scan id forwarding * fix(guardrails): prevent overwriting existing metadata headers when extracting scan id * test(guardrails): cover header merging logic * chore(guardrails): fix formatting * test(guardrails): enforce case preservation * chore(guardrails): corrected grayswan type annotations * fix(guardrails): sanitized grayswan header metadata * test(guardrails): covered grayswan logging headers * fix(guardrails): guard grayswan header lookup against None and drop dead comment - Fall back to {} when proxy_server_request is explicitly None so request_data.get(...).get('headers') never raises AttributeError. - Remove the commented-out user_api_key_auth pop; it was inert and greptile called it out as ambiguous. --------- Co-authored-by: Theodore Drzewinski <93957989+tediferJones@users.noreply.github.com> --- .../guardrail_hooks/grayswan/grayswan.py | 48 +++++- .../guardrail_hooks/test_grayswan.py | 147 ++++++++++++++---- 2 files changed, 159 insertions(+), 36 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py b/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py index a14d2fc8608..9805b1a9117 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py +++ b/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py @@ -213,7 +213,7 @@ class GraySwanGuardrail(CustomGuardrail): verbose_proxy_logger.debug("Gray Swan Guardrail: dynamic extra_body=%s", safe_dumps(dynamic_body)) # Prepare and send payload - payload = self._prepare_payload(messages, dynamic_body, request_data) + payload = self._prepare_payload(messages, dynamic_body, request_data, logging_obj) if payload is None: return inputs @@ -502,10 +502,38 @@ class GraySwanGuardrail(CustomGuardrail): "grayswan-api-key": self.api_key, } + def _extract_inbound_headers( + self, + request_data: dict, + logging_obj: Optional["LiteLLMLoggingObj"] = None, + ) -> Optional[dict[str, str]]: + headers = (request_data.get("proxy_server_request") or {}).get("headers") + if not headers: + headers = request_data.get("headers") + if not headers: + headers = (request_data.get("metadata") or {}).get("headers") + if not headers and logging_obj and getattr(logging_obj, "model_call_details", None): + headers = ( + (logging_obj.model_call_details or {}).get("litellm_params", {}).get("metadata", {}).get("headers") + ) + if not isinstance(headers, dict): + return None + + forwarded_header_names = ("shade_scan_id",) + forwarded_headers = {} + for key, value in headers.items(): + if str(key).lower() in forwarded_header_names: + forwarded_headers[str(key)] = str(value) + return forwarded_headers or None + def _prepare_payload( - self, messages: List[Dict[str, str]], dynamic_body: dict, request_data: dict - ) -> Optional[Dict[str, Any]]: - payload: Dict[str, Any] = {"messages": messages} + self, + messages: list[dict[str, str]], + dynamic_body: dict, + request_data: dict, + logging_obj: Optional["LiteLLMLoggingObj"] = None, + ) -> Optional[dict[str, Any]]: + payload: dict[str, Any] = {"messages": messages} categories = dynamic_body.get("categories") or self.categories if categories: @@ -523,10 +551,16 @@ class GraySwanGuardrail(CustomGuardrail): if "metadata" in dynamic_body: payload["metadata"] = dynamic_body["metadata"] + inbound_headers = self._extract_inbound_headers(request_data, logging_obj) + litellm_metadata = request_data.get("litellm_metadata") - if isinstance(litellm_metadata, dict) and litellm_metadata: - cleaned_litellm_metadata = dict(litellm_metadata) - # cleaned_litellm_metadata.pop("user_api_key_auth", None) + cleaned_litellm_metadata = dict(litellm_metadata) if isinstance(litellm_metadata, dict) else {} + if inbound_headers: + existing_headers = cleaned_litellm_metadata.get("headers") + cleaned_litellm_metadata["headers"] = ( + {**existing_headers, **inbound_headers} if isinstance(existing_headers, dict) else inbound_headers + ) + if cleaned_litellm_metadata: sanitized = safe_json_loads(safe_dumps(cleaned_litellm_metadata), default={}) if isinstance(sanitized, dict) and sanitized: payload["litellm_metadata"] = sanitized diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py index f2e7447239f..53af7f36a5f 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py @@ -5,6 +5,7 @@ from fastapi import HTTPException from litellm.integrations.custom_guardrail import ModifyResponseException from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.guardrails.guardrail_hooks.grayswan import grayswan as grayswan_module from litellm.proxy.guardrails.guardrail_hooks.grayswan.grayswan import ( GraySwanGuardrail, GraySwanGuardrailAPIError, @@ -70,12 +71,118 @@ def test_prepare_payload_includes_dynamic_metadata( assert payload["metadata"] == dynamic_body["metadata"] +def test_prepare_payload_forwards_only_scan_id_header( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = { + "proxy_server_request": { + "headers": { + "SHADE_SCAN_ID": "scan-123", + "authorization": "Bearer secret", + } + }, + "litellm_metadata": {"request_id": "request-123"}, + } + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data) + + assert payload["litellm_metadata"] == { + "request_id": "request-123", + "headers": {"SHADE_SCAN_ID": "scan-123"}, + } + + +def test_prepare_payload_merges_scan_id_with_existing_metadata_headers( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = { + "proxy_server_request": { + "headers": { + "shade_scan_id": "scan-123", + } + }, + "litellm_metadata": { + "request_id": "request-123", + "headers": {"x-existing": "keep-me"}, + }, + } + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data) + + assert payload["litellm_metadata"] == { + "request_id": "request-123", + "headers": { + "x-existing": "keep-me", + "shade_scan_id": "scan-123", + }, + } + + +def test_prepare_payload_sanitizes_headers_when_litellm_metadata_absent( + monkeypatch: pytest.MonkeyPatch, + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = { + "proxy_server_request": { + "headers": { + "shade_scan_id": "scan-123", + } + } + } + + monkeypatch.setattr(grayswan_module, "safe_dumps", lambda _data: "{}") + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data) + + assert "litellm_metadata" not in payload + + +def test_prepare_payload_extracts_headers_from_logging_obj( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = {} + logging_obj = type( + "LoggingObj", + (), + { + "model_call_details": { + "litellm_params": { + "metadata": { + "headers": { + "shade_scan_id": "scan-from-logging", + "authorization": "Bearer secret", + } + } + } + } + }, + )() + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data, logging_obj) + + assert payload["litellm_metadata"] == { + "headers": {"shade_scan_id": "scan-from-logging"}, + } + + +def test_prepare_payload_ignores_logging_obj_without_model_call_details( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + + payload = grayswan_guardrail._prepare_payload(messages, {}, {}, object()) + + assert "litellm_metadata" not in payload + + def test_process_response_does_not_block_under_threshold( grayswan_guardrail: GraySwanGuardrail, ) -> None: - grayswan_guardrail._process_grayswan_response( - {"violation": 0.3, "violated_rules": []} - ) + grayswan_guardrail._process_grayswan_response({"violation": 0.3, "violated_rules": []}) def test_process_response_blocks_when_threshold_exceeded() -> None: @@ -127,16 +234,12 @@ class _DummyClient: self.calls: list[dict] = [] async def post(self, *, url: str, headers: dict, json: dict, timeout: float): - self.calls.append( - {"url": url, "headers": headers, "json": json, "timeout": timeout} - ) + self.calls.append({"url": url, "headers": headers, "json": json, "timeout": timeout}) return _DummyResponse(self.payload) @pytest.mark.asyncio -async def test_run_guardrail_posts_payload( - monkeypatch, grayswan_guardrail: GraySwanGuardrail -) -> None: +async def test_run_guardrail_posts_payload(monkeypatch, grayswan_guardrail: GraySwanGuardrail) -> None: dummy_client = _DummyClient({"violation": 0.1}) grayswan_guardrail.async_handler = dummy_client @@ -308,9 +411,7 @@ def test_process_response_passthrough_raises_exception_in_pre_call() -> None: # Should raise ModifyResponseException with pytest.raises(ModifyResponseException) as exc: - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.pre_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.pre_call) assert "Gray Swan Cygnal Guardrail" in exc.value.message assert exc.value.model == "gpt-4" @@ -338,9 +439,7 @@ def test_process_response_passthrough_raises_exception_in_during_call() -> None: # Should raise ModifyResponseException with pytest.raises(ModifyResponseException) as exc: - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.during_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.during_call) assert "Gray Swan Cygnal Guardrail" in exc.value.message assert exc.value.model == "gpt-4" @@ -365,9 +464,7 @@ def test_process_response_passthrough_stores_detection_info_in_post_call() -> No } # Should NOT raise an exception in post_call - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.post_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.post_call) # Verify detection info was stored in metadata assert "metadata" in data @@ -400,9 +497,7 @@ def test_process_response_passthrough_does_not_raise_if_under_threshold() -> Non } # Should not raise an exception since under threshold - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.pre_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.pre_call) # Should not have any detection info since it didn't exceed threshold assert "guardrail_detections" not in data.get("metadata", {}) @@ -436,10 +531,7 @@ def test_format_violation_message() -> None: assert "Gray Swan Cygnal Guardrail" in message assert "the input query has a violation score of 0.85" in message assert "violating the rule(s): 1, 3, 5" in message - assert ( - "Mutation effort to make the harmful intention disguised was DETECTED" - in message - ) + assert "Mutation effort to make the harmful intention disguised was DETECTED" in message # IPI should not be in message since it's False assert "Indirect Prompt Injection was DETECTED" not in message @@ -450,10 +542,7 @@ def test_format_violation_message() -> None: assert "Gray Swan Cygnal Guardrail" in message assert "the model response has a violation score of 0.85" in message assert "violating the rule(s): 1, 3, 5" in message - assert ( - "Mutation effort to make the harmful intention disguised was DETECTED" - in message - ) + assert "Mutation effort to make the harmful intention disguised was DETECTED" in message def test_prepare_payload_includes_litellm_metadata( From 641396762ac8e363325ae1e177e8079e4edec9e4 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 15:17:13 -0700 Subject: [PATCH 026/399] refactor(ui): conform license banner to new eslint rules Staging recently added the local eslint rules no-large-inline-object-arg and no-long-condition-chain and tightened no-nested-ternary to an error. After merging staging, the license-banner code tripped them: the banner's tiered description was a nested ternary (now an error), and two option objects were passed inline (adding budget debt). Extract the description into an early-return helper, and hoist the useQuery options and the date-format options into named constants. No behavior change; keeps the inline-object-arg count at the committed baseline rather than bumping it --- .../hooks/license/useLicenseInfo.ts | 5 +++-- .../src/components/LicenseExpiryBanner.tsx | 19 +++++++++++-------- .../src/utils/licenseUtils.ts | 14 ++++++++------ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts index f4574c36ef6..3ea0bd20e40 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/license/useLicenseInfo.ts @@ -5,11 +5,12 @@ import { createQueryKeys } from "../common/queryKeysFactory"; const licenseInfoKeys = createQueryKeys("licenseInfo"); export const useLicenseInfo = (accessToken: string | null | undefined): UseQueryResult => { - return useQuery({ + const options = { queryKey: licenseInfoKeys.detail("license"), queryFn: () => getLicenseInfo(accessToken!), enabled: Boolean(accessToken), staleTime: 5 * 60 * 1000, retry: false, - }); + }; + return useQuery(options); }; diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx index e5b8a65168a..c3b20b5fac0 100644 --- a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx @@ -29,6 +29,16 @@ const describeCountdown = (days: number): string => { return `expires in ${days} days`; }; +const expiryDescription = (tier: "warning" | "critical" | "expired"): React.ReactNode => { + if (tier === "expired") { + return <>Enterprise features are now disabled. Reach out to {salesLink} to restore access; + } + if (tier === "critical") { + return <>Renew now to avoid losing enterprise features. Reach out to {salesLink}; + } + return <>Renew before it lapses to keep enterprise features. Reach out to {salesLink}; +}; + export const LicenseExpiryBannerView: React.FC = ({ licenseInfo }) => { const [locallyDismissed, setLocallyDismissed] = useState(false); @@ -56,14 +66,7 @@ export const LicenseExpiryBannerView: React.FC = ( ? `Your LiteLLM Enterprise license expired on ${formattedDate}` : `Your LiteLLM Enterprise license ${describeCountdown(days)} (${formattedDate})`; - const description = - tier === "expired" ? ( - <>Enterprise features are now disabled. Reach out to {salesLink} to restore access - ) : tier === "critical" ? ( - <>Renew now to avoid losing enterprise features. Reach out to {salesLink} - ) : ( - <>Renew before it lapses to keep enterprise features. Reach out to {salesLink} - ); + const description = expiryDescription(tier); const handleClose = () => { if (typeof window !== "undefined") { diff --git a/ui/litellm-dashboard/src/utils/licenseUtils.ts b/ui/litellm-dashboard/src/utils/licenseUtils.ts index b2681664c56..57acad85508 100644 --- a/ui/litellm-dashboard/src/utils/licenseUtils.ts +++ b/ui/litellm-dashboard/src/utils/licenseUtils.ts @@ -35,15 +35,17 @@ export const getLicenseExpiryTier = (expirationDate: string | null, now: Date = return "none"; }; +const EXPIRY_DATE_FORMAT: Intl.DateTimeFormatOptions = { + year: "numeric", + month: "short", + day: "numeric", + timeZone: "UTC", +}; + export const formatExpiryDate = (expirationDate: string): string => { const date = new Date(`${expirationDate}T00:00:00Z`); if (Number.isNaN(date.getTime())) { return expirationDate; } - return date.toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - timeZone: "UTC", - }); + return date.toLocaleDateString("en-US", EXPIRY_DATE_FORMAT); }; From bd23c44cb197e71143c4bae838b8af0da1869971 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Wed, 8 Jul 2026 15:28:28 -0700 Subject: [PATCH 027/399] refactor(ui): consolidate table cells onto a shared table_cells kit (#32393) * feat(ui): add shared table_cells kit and convert logs columns DateCell, MoneyCell, IdCell and StatusBadge consolidate the duplicated per-table cell implementations behind one component each. The logs page columns are the reference conversion; the dead auditLogColumns export (superseded by audit_logs.tsx) is removed with it * refactor(ui): consolidate table cells onto the shared table_cells kit 106 cell sites across 44 table files converge onto DateCell, MoneyCell, IdCell and StatusBadge, replacing 8 date formats, 6 spend formats, 7 id truncation strategies and 6 status badge styles with one implementation each. Badge now forwards refs so Base UI tooltip triggers composed over it can attach (they previously never opened under React 18). TimeCell is deleted; its two consumers now render DateCell * fix(ui): suppress cost tooltip for zero spend and drop dead getStatusBadge param The logs Cost tooltip showed the raw $0 over a "-" cell for zero or null spend (pre-existing, surfaced by review); the tooltip now only renders when there is a real amount. healthCheckColumns no longer takes the unused getStatusBadge callback and its dead definition is removed * fix(ui): restyle StatusBadge as tinted pill matching the prior antd Tag look * fix(ui): keep StatusBadge fully rounded like the other kit pills --- ui/litellm-dashboard/eslint-metrics.json | 4 +- ui/litellm-dashboard/eslint-suppressions.json | 7 +- .../components/AccessGroupsPage.tsx | 21 +- .../budgets/components/budget_panel.tsx | 5 +- .../memory/components/MemoryView.tsx | 32 +-- .../projects/components/ProjectKeysTable.tsx | 5 +- .../projects/components/ProjectsPage.tsx | 18 +- .../prompts/components/prompt_table.tsx | 59 +---- .../_components/SearchToolColumn.tsx | 14 +- .../users/_components/BulkEditUsers.test.tsx | 2 +- .../users/_components/BulkEditUsers.tsx | 3 +- .../users/_components/view_users.test.tsx | 3 +- .../users/_components/view_users/columns.tsx | 42 +--- .../components/AIHub/AgentHubTableColumns.tsx | 13 +- .../DeletedKeysTable/DeletedKeysTable.tsx | 49 +---- .../DeletedTeamsTable/DeletedTeamsTable.tsx | 50 +---- .../src/components/OldTeams.tsx | 23 +- .../LoggingCallbacksTable.tsx | 14 +- .../src/components/ToolPolicies.tsx | 14 +- .../components/EndpointUsageTable.test.tsx | 6 - .../components/EndpointUsageTable.tsx | 4 +- .../components/EntityUsage/EntityUsage.tsx | 9 +- .../EntityUsage/SpendByProvider.test.tsx | 10 +- .../EntityUsage/SpendByProvider.tsx | 5 +- .../EntityUsage/TopKeyView.test.tsx | 27 +-- .../components/EntityUsage/TopKeyView.tsx | 23 +- .../EntityUsage/TopModelView.test.tsx | 2 +- .../components/EntityUsage/TopModelView.tsx | 6 +- .../components/KeyModelUsageView.test.tsx | 2 +- .../components/KeyModelUsageView.tsx | 3 +- .../VirtualKeysPage/VirtualKeysTable.test.tsx | 10 +- .../VirtualKeysPage/VirtualKeysTable.tsx | 79 ++----- .../src/components/agents.tsx | 21 +- .../claude_code_plugins/plugin_table.tsx | 34 +-- .../src/components/general_settings.tsx | 12 +- .../components/guardrails/guardrail_table.tsx | 52 +---- .../src/components/mcp_hub_table_columns.tsx | 21 +- .../components/mcp_tools/MCPToolsetsTab.tsx | 13 +- .../model_dashboard/HealthCheckComponent.tsx | 18 +- .../model_dashboard/health_check_columns.tsx | 25 ++- .../components/model_hub_table_columns.tsx | 9 +- .../molecules/models/columns.test.tsx | 58 +++++ .../components/molecules/models/columns.tsx | 54 ++--- .../organization/organization_view.tsx | 3 +- .../src/components/organizations.tsx | 32 ++- .../src/components/pass_through_settings.tsx | 16 +- .../policies/attachment_table.test.tsx | 7 +- .../components/policies/attachment_table.tsx | 25 +-- .../src/components/policies/policy_table.tsx | 16 +- .../shared/table_cells/cell_tooltip.tsx | 21 ++ .../shared/table_cells/date_cell.test.tsx | 57 +++++ .../shared/table_cells/date_cell.tsx | 41 ++++ .../shared/table_cells/id_cell.test.tsx | 78 +++++++ .../components/shared/table_cells/id_cell.tsx | 94 ++++++++ .../components/shared/table_cells/index.ts | 5 + .../shared/table_cells/money_cell.test.tsx | 44 ++++ .../shared/table_cells/money_cell.tsx | 23 ++ .../shared/table_cells/status_badge.test.tsx | 42 ++++ .../shared/table_cells/status_badge.tsx | 38 ++++ .../components/skill_hub_table_columns.tsx | 8 +- .../tag_management/TagTable.test.tsx | 18 +- .../components/tag_management/TagTable.tsx | 39 +--- .../components/team/TeamMemberTab.test.tsx | 27 ++- .../src/components/team/TeamMemberTab.tsx | 36 +--- .../components/team/TeamVirtualKeysTable.tsx | 76 ++----- .../src/components/ui/badge.tsx | 22 +- ui/litellm-dashboard/src/components/usage.tsx | 9 +- .../DocumentsTable.tsx | 17 +- .../VectorStoreTable.test.tsx | 9 +- .../VectorStoreTable.tsx | 25 +-- .../src/components/view_logs/audit_logs.tsx | 12 +- .../src/components/view_logs/columns.test.tsx | 56 +++++ .../src/components/view_logs/columns.tsx | 202 ++---------------- .../components/view_logs/time_cell.test.tsx | 36 ---- .../src/components/view_logs/time_cell.tsx | 42 ---- 75 files changed, 918 insertions(+), 1139 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/cell_tooltip.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/date_cell.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/date_cell.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/id_cell.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/index.ts create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/money_cell.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/money_cell.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/status_badge.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/status_badge.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/columns.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/view_logs/time_cell.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/view_logs/time_cell.tsx diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index f4dc89c5b80..ded6ab97e1e 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,7 +1,7 @@ { - "@typescript-eslint/no-explicit-any": 1988, + "@typescript-eslint/no-explicit-any": 1982, "complexity": 128, - "local/no-large-inline-object-arg": 513, + "local/no-large-inline-object-arg": 512, "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index b077338c75b..fe8f182c106 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1650,7 +1650,7 @@ "count": 1 }, "no-restricted-imports": { - "count": 2 + "count": 1 } }, "src/components/guardrails/tool_permission/ToolPermissionRulesEditor.tsx": { @@ -2491,11 +2491,6 @@ "count": 4 } }, - "src/components/view_logs/columns.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/view_logs/index.tsx": { "no-restricted-imports": { "count": 1 diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx index c3dd1d54b32..dbbf4e35900 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx @@ -19,6 +19,7 @@ import { SortState, TableHeaderSortDropdown, } from "@/components/common_components/TableHeaderSortDropdown/TableHeaderSortDropdown"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { AccessGroupDetail } from "./AccessGroupsDetailsPage"; import { AccessGroupCreateModal } from "./AccessGroupsModal/AccessGroupCreateModal"; import { AccessGroup } from "./types"; @@ -143,21 +144,7 @@ export function AccessGroupsPage() { header: () => ID, enableSorting: false, size: 170, - cell: ({ row }) => { - const record = row.original; - return ( - - setSelectedGroupId(record.id)} - > - {record.id} - - - ); - }, + cell: ({ row }) => , }, { id: "name", @@ -211,7 +198,7 @@ export function AccessGroupsPage() { header: () => Created, enableSorting: true, sortingFn: "datetime", - cell: ({ getValue }) => new Date(getValue() as string).toLocaleDateString(), + cell: ({ getValue }) => , meta: { responsive: ["lg"] }, }, { @@ -219,7 +206,7 @@ export function AccessGroupsPage() { accessorKey: "updatedAt", header: () => Updated, enableSorting: false, - cell: ({ getValue }) => new Date(getValue() as string).toLocaleDateString(), + cell: ({ getValue }) => , meta: { responsive: ["xl"] }, }, ...(canModify diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx index 0e601645c20..af15a99f0b4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx @@ -25,6 +25,7 @@ import DeleteResourceModal from "@/components/common_components/DeleteResourceMo import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import NotificationsManager from "@/components/molecules/notifications_manager"; import { useBudgets, useDeleteBudget, budgetItem } from "@/app/(dashboard)/hooks/budgets/useBudgets"; +import { MoneyCell } from "@/components/shared/table_cells"; import BudgetModal from "./budget_modal"; import EditBudgetModal from "./edit_budget_modal"; import { CREATE_END_USER_CURL_COMMAND, CHAT_COMPLETIONS_CURL_COMMAND, OPENAI_SDK_PYTHON_CODE } from "./constants"; @@ -127,7 +128,9 @@ const BudgetPanel: React.FC = ({ accessToken }) => { .map((value: budgetItem) => ( {value.budget_id} - {value.max_budget ? value.max_budget : "n/a"} + + + {value.tpm_limit ? value.tpm_limit : "n/a"} {value.rpm_limit ? value.rpm_limit : "n/a"} {canModify && ( diff --git a/ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryView.tsx index 402de29e0c5..4ee784f4664 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryView.tsx @@ -2,7 +2,7 @@ import React, { useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { Button, Card, Drawer, Empty, Input, Space, Table, Tooltip, Typography, message } from "antd"; +import { Button, Card, Drawer, Empty, Input, Space, Table, Typography, message } from "antd"; import type { ColumnsType } from "antd/es/table"; import { DeleteOutlined, @@ -13,6 +13,7 @@ import { SearchOutlined, } from "@ant-design/icons"; import { MemoryRow, createMemory, deleteMemory, fetchMemoryList, updateMemory } from "@/components/networking"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { MemoryEditModal } from "./MemoryEditModal"; import DeleteResourceModal from "@/components/common_components/DeleteResourceModal"; @@ -191,34 +192,13 @@ export const MemoryView: React.FC = ({ accessToken }) => { } }; - const renderIdPill = (id: string | null | undefined, onClick?: () => void) => { - if (!id) return -; - const short = id.length > 10 ? `${id.slice(0, 7)}...` : id; - const pillClass = - "font-mono text-blue-600 bg-blue-50 text-xs font-medium px-2 py-0.5 rounded-md border border-blue-200 inline-block max-w-[15ch] truncate whitespace-nowrap"; - return ( - - {onClick ? ( - - ) : ( - {short} - )} - - ); - }; - const columns: ColumnsType = [ { title: "ID", dataIndex: "memory_id", key: "memory_id", width: 140, - render: (_: unknown, r: MemoryRow) => renderIdPill(r.memory_id, () => setDetailRow(r)), + render: (_: unknown, r: MemoryRow) => setDetailRow(r)} />, }, { title: "Name", @@ -246,21 +226,21 @@ export const MemoryView: React.FC = ({ accessToken }) => { dataIndex: "user_id", key: "user_id", width: 160, - render: (uid?: string | null) => renderIdPill(uid), + render: (uid?: string | null) => , }, { title: "Team ID", dataIndex: "team_id", key: "team_id", width: 160, - render: (tid?: string | null) => renderIdPill(tid), + render: (tid?: string | null) => , }, { title: "Updated", dataIndex: "updated_at", key: "updated_at", width: 180, - render: (ts?: string) => {formatTimestamp(ts)}, + render: (ts?: string) => , // No sorter — backend already returns rows in `updated_at DESC` order, // and a client-side sorter on a paginated view would only affect the // current page. diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.tsx index 7b891078d36..8269c843b98 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.tsx @@ -3,6 +3,7 @@ import { Empty, Table, Tooltip } from "antd"; import type { ColumnsType } from "antd/es/table"; import type { SpinProps } from "antd"; import DefaultProxyAdminTag from "@/components/common_components/DefaultProxyAdminTag"; +import { DateCell } from "@/components/shared/table_cells"; interface ProjectKeysTableProps { keys: KeyResponse[]; @@ -33,13 +34,13 @@ const columns: ColumnsType = [ title: "Created", dataIndex: "created_at", key: "created_at", - render: (date: string) => (date ? new Date(date).toLocaleDateString() : "—"), + render: (date: string) => , }, { title: "Last Active", dataIndex: "last_active", key: "last_active", - render: (date: string | null) => (date ? new Date(date).toLocaleDateString() : "Never"), + render: (date: string | null) => , }, ]; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.tsx index 8a3fc178914..be989229022 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.tsx @@ -1,5 +1,6 @@ import { useProjects, ProjectResponse } from "@/app/(dashboard)/hooks/projects/useProjects"; import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { LoadingOutlined, PlusOutlined } from "@ant-design/icons"; import { Button, @@ -72,18 +73,7 @@ export function ProjectsPage() { dataIndex: "project_id", key: "project_id", width: 170, - render: (id: string) => ( - - setSelectedProjectId(id)} - > - {id} - - - ), + render: (id: string) => , }, { title: "Name", @@ -137,14 +127,14 @@ export function ProjectsPage() { key: "created_at", sorter: (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime(), responsive: ["lg"], - render: (date: string) => new Date(date).toLocaleDateString(), + render: (date: string) => , }, { title: "Updated", dataIndex: "updated_at", key: "updated_at", responsive: ["xl"], - render: (date: string) => new Date(date).toLocaleDateString(), + render: (date: string) => , }, ]; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/prompts/components/prompt_table.tsx b/ui/litellm-dashboard/src/app/(dashboard)/prompts/components/prompt_table.tsx index 94b242ca4ea..51a03d19e83 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/prompts/components/prompt_table.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/prompts/components/prompt_table.tsx @@ -2,8 +2,8 @@ import React, { useState, useEffect } from "react"; import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Button } from "@tremor/react"; import { SwitchVerticalIcon, ChevronUpIcon, ChevronDownIcon, TrashIcon } from "@heroicons/react/outline"; import { Tooltip } from "antd"; -import { CopyOutlined } from "@ant-design/icons"; import { PromptSpec, modelHubCall } from "@/components/networking"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { ColumnDef, flexRender, @@ -62,48 +62,11 @@ const PromptTable: React.FC = ({ fetchModelHubData(); }, [accessToken]); - // Format date helper function - const formatDate = (dateString?: string) => { - if (!dateString) return "-"; - const date = new Date(dateString); - return date.toLocaleString(); - }; - - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text); - }; - const columns: ColumnDef[] = [ { header: "Prompt ID", accessorKey: "prompt_id", - cell: (info: any) => { - const fullId = String(info.getValue() || ""); - const displayId = fullId.length > 25 ? `${fullId.slice(0, 25)}...` : fullId; - return ( -
- - - - - { - e.stopPropagation(); - copyToClipboard(fullId); - }} - className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs" - /> - -
- ); - }, + cell: (info: any) => , }, { header: "Model", @@ -162,26 +125,12 @@ const PromptTable: React.FC = ({ { header: "Created At", accessorKey: "created_at", - cell: ({ row }) => { - const prompt = row.original; - return ( - - {formatDate(prompt.created_at)} - - ); - }, + cell: ({ row }) => , }, { header: "Updated At", accessorKey: "updated_at", - cell: ({ row }) => { - const prompt = row.original; - return ( - - {formatDate(prompt.updated_at)} - - ); - }, + cell: ({ row }) => , }, { header: "Environment", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolColumn.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolColumn.tsx index 198b3ea095f..3d9fdb2866e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolColumn.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolColumn.tsx @@ -1,6 +1,7 @@ import { Tag } from "antd"; import { ColumnsType } from "antd/es/table"; import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { SearchTool } from "./types"; export const searchToolColumns = ( @@ -20,14 +21,7 @@ export const searchToolColumns = ( return -; } - return ( - - ); + return ; }, }, { @@ -52,7 +46,7 @@ export const searchToolColumns = ( dataIndex: "created_at", key: "created_at", render: (_, tool) => { - return {tool.created_at ? new Date(tool.created_at).toLocaleDateString() : "-"}; + return ; }, }, { @@ -60,7 +54,7 @@ export const searchToolColumns = ( dataIndex: "updated_at", key: "updated_at", render: (_, tool) => { - return {tool.updated_at ? new Date(tool.updated_at).toLocaleDateString() : "-"}; + return ; }, }, { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.test.tsx index f16f5325952..e49ac854e6d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.test.tsx @@ -91,7 +91,7 @@ describe("BulkEditUserModal", () => { it("should display budget information in table", () => { renderWithProviders(); - expect(screen.getByText("$50")).toBeInTheDocument(); + expect(screen.getByText("$50.00")).toBeInTheDocument(); expect(screen.getByText("Unlimited")).toBeInTheDocument(); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.tsx index 9bef2f3a937..7ea53352807 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.tsx @@ -4,6 +4,7 @@ import { userBulkUpdateUserCall, teamBulkMemberAddCall, Member } from "@/compone import { UserEditView } from "./user_edit_view"; import NotificationsManager from "@/components/molecules/notifications_manager"; import MessageManager from "@/components/molecules/message_manager"; +import { MoneyCell } from "@/components/shared/table_cells"; const { Text, Title } = Typography; @@ -270,7 +271,7 @@ const BulkEditUserModal: React.FC = ({ key: "max_budget", width: "20%", render: (budget: number | null) => ( - {budget !== null ? `$${budget}` : "Unlimited"} + ), }, ]} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx index 241cda3464e..996fd58efc1 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx @@ -158,7 +158,8 @@ describe("ViewUserDashboard", () => { expect( screen.getByText("Are you sure you want to delete this user? This action cannot be undone."), ).toBeInTheDocument(); - expect(screen.getByText("user-1")).toBeInTheDocument(); + const userIdInstances = screen.getAllByText("user-1"); + expect(userIdInstances.length).toBeGreaterThan(0); const emailInstances = screen.getAllByText("test@example.com"); expect(emailInstances.length).toBeGreaterThan(0); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/columns.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/columns.tsx index deeaeeb25f4..fc680cb5b1b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/columns.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/columns.tsx @@ -3,8 +3,7 @@ import { Badge, Grid, Icon } from "@tremor/react"; import { Tooltip, Checkbox, Tag } from "antd"; import { UserInfo } from "@/components/networking"; import { PencilAltIcon, TrashIcon, InformationCircleIcon, RefreshIcon } from "@heroicons/react/outline"; -import { CopyOutlined } from "@ant-design/icons"; -import { formatNumberWithCommas, copyToClipboard } from "@/utils/dataUtils"; +import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; interface SelectionOptions { selectedUsers: UserInfo[]; @@ -29,24 +28,7 @@ export const columns = ( header: "User ID", accessorKey: "user_id", enableSorting: true, - cell: ({ row }) => ( -
- - {row.original.user_id ? `${row.original.user_id.slice(0, 7)}...` : "-"} - - {row.original.user_id && ( - - { - e.stopPropagation(); - copyToClipboard(row.original.user_id, "User ID copied to clipboard"); - }} - className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs" - /> - - )} -
- ), + cell: ({ row }) => , }, { header: "Email", @@ -93,17 +75,13 @@ export const columns = ( header: "Spend (USD)", accessorKey: "spend", enableSorting: true, - cell: ({ row }) => ( - {row.original.spend ? formatNumberWithCommas(row.original.spend, 4) : "-"} - ), + cell: ({ row }) => , }, { header: "Budget (USD)", accessorKey: "max_budget", enableSorting: false, - cell: ({ row }) => ( - {row.original.max_budget !== null ? row.original.max_budget : "Unlimited"} - ), + cell: ({ row }) => , }, { header: () => ( @@ -142,21 +120,13 @@ export const columns = ( header: "Created At", accessorKey: "created_at", enableSorting: true, - cell: ({ row }) => ( - - {row.original.created_at ? new Date(row.original.created_at).toLocaleDateString() : "-"} - - ), + cell: ({ row }) => , }, { header: "Updated At", accessorKey: "updated_at", enableSorting: false, - cell: ({ row }) => ( - - {row.original.updated_at ? new Date(row.original.updated_at).toLocaleDateString() : "-"} - - ), + cell: ({ row }) => , }, { id: "actions", diff --git a/ui/litellm-dashboard/src/components/AIHub/AgentHubTableColumns.tsx b/ui/litellm-dashboard/src/components/AIHub/AgentHubTableColumns.tsx index 09b1c147615..ae1a19ff95d 100644 --- a/ui/litellm-dashboard/src/components/AIHub/AgentHubTableColumns.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/AgentHubTableColumns.tsx @@ -2,6 +2,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { Button, Badge, Text } from "@tremor/react"; import { Tooltip, Tag } from "antd"; import { CopyOutlined, InfoCircleOutlined } from "@ant-design/icons"; +import { StatusBadge } from "@/components/shared/table_cells"; export interface AgentHubData { agent_id?: string; @@ -194,17 +195,9 @@ export const getAgentHubTableColumns = ( return publicA - publicB; }, cell: ({ row }) => { - const agent = row.original; + const isPublic = row.original.is_public === true; - return agent.is_public === true ? ( - - Yes - - ) : ( - - No - - ); + return ; }, meta: { className: "hidden md:table-cell", diff --git a/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx b/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx index bc52bbbe062..d4a120d0589 100644 --- a/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx @@ -1,5 +1,5 @@ "use client"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; import { ChevronDownIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline"; import { ColumnDef, @@ -59,14 +59,7 @@ export function DeletedKeysTable({ header: "Key ID", size: 150, maxSize: 250, - cell: (info) => { - const value = info.getValue() as string; - return ( - - {value || "-"} - - ); - }, + cell: (info) => , }, { id: "key_alias", @@ -100,9 +93,7 @@ export function DeletedKeysTable({ header: "Spend (USD)", size: 100, maxSize: 140, - cell: (info) => ( - {formatNumberWithCommas(info.getValue() as number, 4)} - ), + cell: (info) => , }, { id: "max_budget", @@ -110,14 +101,9 @@ export function DeletedKeysTable({ header: "Budget (USD)", size: 110, maxSize: 150, - cell: (info) => { - const maxBudget = info.getValue() as number | null; - return ( - - {maxBudget === null ? "Unlimited" : `$${formatNumberWithCommas(maxBudget)}`} - - ); - }, + cell: (info) => ( + + ), }, { id: "user_email", @@ -140,14 +126,7 @@ export function DeletedKeysTable({ header: "User ID", size: 120, maxSize: 200, - cell: (info) => { - const userId = info.getValue() as string | null; - return ( - - {userId || "-"} - - ); - }, + cell: (info) => , }, { id: "created_at", @@ -155,12 +134,7 @@ export function DeletedKeysTable({ header: "Created At", size: 120, maxSize: 140, - cell: (info) => { - const value = info.getValue(); - return ( - {value ? new Date(value as string).toLocaleDateString() : "-"} - ); - }, + cell: (info) => , }, { id: "created_by", @@ -183,10 +157,9 @@ export function DeletedKeysTable({ header: "Deleted At", size: 120, maxSize: 140, - cell: (info) => { - const value = (info.row.original as any).deleted_at as string | null | undefined; - return {value ? new Date(value).toLocaleDateString() : "-"}; - }, + cell: (info) => ( + + ), }, { id: "deleted_by", diff --git a/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx b/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx index 57260f6bb2d..ddfd5cf73b6 100644 --- a/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx +++ b/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx @@ -1,5 +1,5 @@ "use client"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; import { ChevronDownIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline"; import { ColumnDef, @@ -51,14 +51,7 @@ export function DeletedTeamsTable({ teams, isLoading, isFetching }: DeletedTeams header: "Team ID", size: 150, maxSize: 250, - cell: (info) => { - const value = info.getValue() as string; - return ( - - {value || "-"} - - ); - }, + cell: (info) => , }, { id: "created_at", @@ -66,12 +59,7 @@ export function DeletedTeamsTable({ teams, isLoading, isFetching }: DeletedTeams header: "Created", size: 120, maxSize: 140, - cell: (info) => { - const value = info.getValue(); - return ( - {value ? new Date(value as string).toLocaleDateString() : "-"} - ); - }, + cell: (info) => , }, { id: "spend", @@ -79,12 +67,7 @@ export function DeletedTeamsTable({ teams, isLoading, isFetching }: DeletedTeams header: "Spend (USD)", size: 100, maxSize: 140, - cell: (info) => { - const spend = (info.row.original as any).spend as number | undefined; - return ( - {spend !== undefined ? formatNumberWithCommas(spend, 4) : "-"} - ); - }, + cell: (info) => , }, { id: "max_budget", @@ -92,14 +75,9 @@ export function DeletedTeamsTable({ teams, isLoading, isFetching }: DeletedTeams header: "Budget (USD)", size: 110, maxSize: 150, - cell: (info) => { - const maxBudget = info.getValue() as number | null; - return ( - - {maxBudget === null || maxBudget === undefined ? "No limit" : `$${formatNumberWithCommas(maxBudget)}`} - - ); - }, + cell: (info) => ( + + ), }, { id: "models", @@ -148,14 +126,7 @@ export function DeletedTeamsTable({ teams, isLoading, isFetching }: DeletedTeams header: "Organization", size: 150, maxSize: 200, - cell: (info) => { - const value = info.getValue() as string; - return ( - - {value || "-"} - - ); - }, + cell: (info) => , }, { id: "deleted_at", @@ -163,10 +134,7 @@ export function DeletedTeamsTable({ teams, isLoading, isFetching }: DeletedTeams header: "Deleted At", size: 120, maxSize: 140, - cell: (info) => { - const value = (info.row.original as any).deleted_at as string | null | undefined; - return {value ? new Date(value).toLocaleDateString() : "-"}; - }, + cell: (info) => , }, { id: "deleted_by", diff --git a/ui/litellm-dashboard/src/components/OldTeams.tsx b/ui/litellm-dashboard/src/components/OldTeams.tsx index c2cb3d4948e..e83d1acf4e5 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.tsx @@ -31,6 +31,7 @@ import type { SorterResult } from "antd/es/table/interface"; import { KeyIcon, LayersIcon, SearchIcon, UsersIcon } from "lucide-react"; import React, { useEffect, useMemo, useRef, useState } from "react"; import { AntDLoadingSpinner } from "@/components/ui/AntDLoadingSpinner"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import OrganizationDropdown from "./common_components/OrganizationDropdown"; import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import { teamListCall as v2TeamListCall, type TeamsResponse } from "@/app/(dashboard)/hooks/teams/useTeams"; @@ -670,18 +671,8 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser key: "team_id", width: 170, ellipsis: true, - render: (id: string, record: Team) => ( - - setSelectedTeamId(record.team_id)} - data-testid="team-id-cell" - > - {id} - - + render: (id: string) => ( + setSelectedTeamId(teamId)} dataTestId="team-id-cell" /> ), }, { @@ -797,13 +788,7 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser width: 130, ellipsis: true, sorter: true, - render: (date: string | undefined) => ( - - {date - ? new Date(date).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" }) - : "—"} - - ), + render: (date: string | undefined) => , }, { title: "Actions", diff --git a/ui/litellm-dashboard/src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx b/ui/litellm-dashboard/src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx index 70ec6599ca2..4f1889cbc99 100644 --- a/ui/litellm-dashboard/src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx +++ b/ui/litellm-dashboard/src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx @@ -3,6 +3,7 @@ import type { TableProps } from "antd"; import { Table } from "antd"; import Title from "antd/es/typography/Title"; import React from "react"; +import { StatusBadge, type StatusTone } from "@/components/shared/table_cells"; import TableIconActionButton from "../../../common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import { AlertingObject } from "./types"; @@ -61,17 +62,8 @@ export const LoggingCallbacksTable: React.FC = ({ // and server-fetched rows both render correctly. const mode = record.type || record.mode || "success"; const label = CALLBACK_MODES.find((m) => m.value === mode)?.label || mode; - const badgeClass = - mode === "success" - ? "bg-green-100 text-green-800" - : mode === "failure" - ? "bg-red-100 text-red-800" - : "bg-blue-100 text-blue-800"; - return ( - - {label} - - ); + const tone: StatusTone = mode === "success" ? "success" : mode === "failure" ? "error" : "info"; + return ; }, width: 240, }, diff --git a/ui/litellm-dashboard/src/components/ToolPolicies.tsx b/ui/litellm-dashboard/src/components/ToolPolicies.tsx index 4bd028f0c8f..4468334f813 100644 --- a/ui/litellm-dashboard/src/components/ToolPolicies.tsx +++ b/ui/litellm-dashboard/src/components/ToolPolicies.tsx @@ -3,7 +3,7 @@ import React, { useCallback, useDeferredValue, useEffect, useMemo, useState } from "react"; import { Button, Switch, Tooltip } from "antd"; import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react"; -import { TimeCell } from "./view_logs/time_cell"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import type { SortState } from "./common_components/TableHeaderSortDropdown/TableHeaderSortDropdown"; import { TableHeaderSortDropdown } from "./common_components/TableHeaderSortDropdown/TableHeaderSortDropdown"; import FilterComponent, { FilterOption } from "./molecules/filter"; @@ -461,7 +461,7 @@ export const ToolPolicies: React.FC = ({ accessToken, onSelec paginated.map((tool) => ( - +
- - {tool.team_id ?? "-"} - + - - - {tool.key_hash ?? "-"} - - + diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx index 793e4c6e3cf..63c606eb00e 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx @@ -31,12 +31,6 @@ vi.mock("antd", async () => { return { Table, Progress }; }); -vi.mock("@/utils/dataUtils", () => ({ - formatNumberWithCommas: (value: number, decimals?: number) => { - return value.toFixed(decimals || 0); - }, -})); - describe("EndpointUsageTable", () => { it("should render", () => { const mockEndpointData = { diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx index f5cfb553370..88f92360ab5 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Table, Progress } from "antd"; import type { ColumnsType } from "antd/es/table"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { MoneyCell } from "@/components/shared/table_cells"; import { MetricWithMetadata } from "../../../types"; interface EndpointUsageTableProps { @@ -112,7 +112,7 @@ const EndpointUsageTable: React.FC = ({ endpointData }) title: "Spend", dataIndex: "spend", key: "spend", - render: (value: number) => `$${formatNumberWithCommas(value, 2)}`, + render: (value: number) => , }, ]; diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx index df5b14a57d6..9dd72f44f67 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx @@ -1,4 +1,5 @@ import useTeams from "@/app/(dashboard)/hooks/useTeams"; +import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { BarChart, @@ -665,7 +666,9 @@ const EntityUsage: React.FC = ({ accessToken, entityType, enti .map((entity) => ( {entity.metadata.alias} - ${formatNumberWithCommas(entity.metrics.spend, 4)} + + + {entity.metrics.successful_requests.toLocaleString()} @@ -777,7 +780,9 @@ const EntityUsage: React.FC = ({ accessToken, entityType, enti {provider.provider}
- ${formatNumberWithCommas(provider.spend, 2)} + + + {provider.successful_requests.toLocaleString()} diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.test.tsx index 0541a9c6925..7eea7653ef1 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from "@testing-library/react"; +import { fireEvent, render, screen } from "@testing-library/react"; import { describe, expect, it, vi, beforeEach } from "vitest"; import SpendByProvider from "./SpendByProvider"; @@ -200,6 +200,14 @@ describe("SpendByProvider", () => { expect(screen.getByText("1,234,567")).toBeInTheDocument(); }); + it("should render zero spend as a dash when Show Zero Spend is on", () => { + render(); + fireEvent.click(screen.getAllByRole("switch")[0]); + expect(screen.getAllByText("google").length).toBeGreaterThan(0); + expect(screen.getByText("-")).toBeInTheDocument(); + expect(screen.queryByText("$0.00")).not.toBeInTheDocument(); + }); + it("should filter data correctly when both toggles are off", () => { render(); expect(screen.getAllByText("openai").length).toBeGreaterThan(0); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx index 58d673bb7ad..5cea84affb9 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx @@ -1,3 +1,4 @@ +import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { InfoCircleOutlined } from "@ant-design/icons"; import { @@ -109,7 +110,9 @@ const SpendByProvider: React.FC = ({ loading, isDateChangi {provider.provider} - ${formatNumberWithCommas(provider.spend, 2)} + + + {provider.successful_requests.toLocaleString()} {provider.failed_requests.toLocaleString()} {provider.tokens.toLocaleString()} diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx index 126bf51bd36..766f027434f 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx @@ -171,7 +171,9 @@ describe("TopKeyView", () => { ]} />, ); - expect(screen.getByText(/sk-1234\.\.\./)).toBeInTheDocument(); + const keyId = screen.getByText("sk-1234567890abcdef"); + expect(keyId).toBeInTheDocument(); + expect(keyId).toHaveClass("truncate"); }); it("should display dash for missing key alias", () => { @@ -206,7 +208,7 @@ describe("TopKeyView", () => { expect(screen.getByText("$123.46")).toBeInTheDocument(); }); - it("should display less than 0.01 spend as <$0.01", () => { + it("should display sub-cent spend as < $0.01", () => { render( { { api_key: "key-123", key_alias: "Test Key", - spend: 0.005, + spend: 0.004, }, ]} />, ); - expect(screen.getByText("<$0.01")).toBeInTheDocument(); + expect(screen.getByText("< $0.01")).toBeInTheDocument(); }); - it("should display zero spend correctly", () => { + it("should display zero spend as a dash", () => { render( { ]} />, ); - expect(screen.getByText("$0.00")).toBeInTheDocument(); + expect(screen.getByText("-")).toBeInTheDocument(); + expect(screen.queryByText("$0.00")).not.toBeInTheDocument(); }); it("should display dash for empty tags", () => { @@ -376,7 +379,7 @@ describe("TopKeyView", () => { />, ); - const keyIdButton = screen.getByText(/key-123\.\.\./).closest("button"); + const keyIdButton = screen.getByText("key-123").closest("button"); if (keyIdButton) { await user.click(keyIdButton); } @@ -410,7 +413,7 @@ describe("TopKeyView", () => { />, ); - const keyIdButton = screen.getByText(/key-123\.\.\./).closest("button"); + const keyIdButton = screen.getByText("key-123").closest("button"); if (keyIdButton) { await user.click(keyIdButton); } @@ -447,7 +450,7 @@ describe("TopKeyView", () => { />, ); - const keyIdButton = screen.getByText(/key-123\.\.\./).closest("button"); + const keyIdButton = screen.getByText("key-123").closest("button"); if (keyIdButton) { await user.click(keyIdButton); } @@ -483,7 +486,7 @@ describe("TopKeyView", () => { />, ); - const keyIdButton = screen.getByText(/key-123\.\.\./).closest("button"); + const keyIdButton = screen.getByText("key-123").closest("button"); if (keyIdButton) { await user.click(keyIdButton); } @@ -522,7 +525,7 @@ describe("TopKeyView", () => { />, ); - const keyIdButton = screen.getByText(/key-123\.\.\./).closest("button"); + const keyIdButton = screen.getByText("key-123").closest("button"); if (keyIdButton) { await user.click(keyIdButton); } @@ -552,7 +555,7 @@ describe("TopKeyView", () => { />, ); - const keyIdButton = screen.getByText(/key-123\.\.\./).closest("button"); + const keyIdButton = screen.getByText("key-123").closest("button"); if (keyIdButton) { await user.click(keyIdButton); } diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx index 40bc41b3e8c..2dcf98a1e5c 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.tsx @@ -1,6 +1,7 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; +import { IdCell, MoneyCell } from "@/components/shared/table_cells"; import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/outline"; -import { BarChart, Button } from "@tremor/react"; +import { BarChart } from "@tremor/react"; import { Segmented, Tooltip } from "antd"; import React, { useState } from "react"; import { formatNumberWithCommas } from "../../../../utils/dataUtils"; @@ -83,20 +84,7 @@ const TopKeyView: React.FC = ({ topKeys, teams, showTags = fals { header: "Key ID", accessorKey: "api_key", - cell: (info: any) => ( -
- - - -
- ), + cell: (info: any) => handleKeyClick(info.row.original)} />, }, { header: "Key Alias", @@ -165,10 +153,7 @@ const TopKeyView: React.FC = ({ topKeys, teams, showTags = fals header: "Spend (USD)", accessorKey: "spend", meta: { numeric: true }, - cell: (info: any) => { - const value = info.getValue(); - return value > 0 && value < 0.01 ? "<$0.01" : `$${formatNumberWithCommas(value, 2)}`; - }, + cell: (info: any) => , }; const columns = showTags ? [...baseColumns, tagsColumn, spendColumn] : [...baseColumns, spendColumn]; diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.test.tsx index f6014d025ae..bbf9379f5e1 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.test.tsx @@ -175,7 +175,7 @@ describe("TopModelView", () => { setTopModelsLimit={mockSetTopModelsLimit} />, ); - expect(screen.getByText("$0.00")).toBeInTheDocument(); + expect(screen.getByText("-")).toBeInTheDocument(); expect(screen.getAllByText("0").length).toBeGreaterThan(0); }); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx index 7562ef06a03..8938767c02c 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopModelView.tsx @@ -1,6 +1,7 @@ import { BarChart } from "@tremor/react"; import { Segmented } from "antd"; import { useState } from "react"; +import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "../../../../utils/dataUtils"; import { DataTable } from "../../../view_logs/table"; @@ -31,10 +32,7 @@ export default function TopModelView({ topModels, topModelsLimit, setTopModelsLi header: "Spend (USD)", accessorKey: "spend", meta: { numeric: true }, - cell: (info: any) => { - const value = info.getValue(); - return `$${formatNumberWithCommas(value, 2)}`; - }, + cell: (info: any) => , }, { header: "Successful", diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx index 61968294e18..322f00a501a 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx @@ -158,7 +158,7 @@ describe("KeyModelUsageView", () => { }, ]; render(); - expect(screen.getByText("$0.00")).toBeInTheDocument(); + expect(screen.getByText("-")).toBeInTheDocument(); expect(screen.getAllByText("0").length).toBeGreaterThan(0); }); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.tsx index ee1a49051da..ceb00e8a19f 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.tsx @@ -1,3 +1,4 @@ +import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { BarChart, Card, Title } from "@tremor/react"; import { Table } from "antd"; @@ -24,7 +25,7 @@ const columns: ColumnsType = [ title: "Spend (USD)", dataIndex: "spend", key: "spend", - render: (value) => `$${formatNumberWithCommas(value, 2)}`, + render: (value) => , }, { title: "Successful", diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx index ba616a03fd9..02f5d588149 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx @@ -1,4 +1,5 @@ -import { act, screen, waitFor, within, fireEvent } from "@testing-library/react"; +import { screen, waitFor, within, fireEvent } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { vi, it, expect, beforeEach, describe, MockedFunction } from "vitest"; import { renderWithProviders } from "../../../tests/test-utils"; import { VirtualKeysTable } from "./VirtualKeysTable"; @@ -175,7 +176,7 @@ it("should display key information correctly", async () => { await waitFor(() => { expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); expect(screen.getByText("Test Team")).toBeInTheDocument(); - expect(screen.getByText("5.5000")).toBeInTheDocument(); + expect(screen.getByText("$5.5000")).toBeInTheDocument(); }); }); @@ -477,9 +478,8 @@ describe("Status column reflects key.blocked / scim_blocked metadata", () => { const tag = await screen.findByTestId(`key-status-${mockKey.token_id}`); expect(tag).toHaveTextContent("Blocked"); - act(() => { - fireEvent.mouseEnter(tag); - }); + const user = userEvent.setup(); + await user.hover(tag); await waitFor(() => { expect(screen.getByText(/Blocked by SCIM/i)).toBeInTheDocument(); }); diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index a803d78ed57..00f4304c8a9 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -13,20 +13,10 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; -import { - Badge, - Button, - Icon, - Table, - TableBody, - TableCell, - TableHead, - TableHeaderCell, - TableRow, - Text, -} from "@tremor/react"; +import { Badge, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react"; import { InfoCircleOutlined, SyncOutlined } from "@ant-design/icons"; -import { Button as AntButton, Popover, Skeleton, Tag, Tooltip, Typography } from "antd"; +import { Button as AntButton, Popover, Skeleton, Typography } from "antd"; +import { DateCell, IdCell, MoneyCell, StatusBadge } from "@/components/shared/table_cells"; import React, { useDeferredValue, useMemo, useState } from "react"; import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; import { PaginatedKeyAliasSelect } from "../KeyAliasSelect/PaginatedKeyAliasSelect/PaginatedKeyAliasSelect"; @@ -145,23 +135,7 @@ export function VirtualKeysTable() { header: "Key ID", size: 100, enableSorting: true, - cell: (info) => { - const value = info.getValue() as string; - const width = info.cell.column.getSize(); - return ( - - - - ); - }, + cell: (info) => setSelectedKey(info.row.original)} />, }, { id: "key_alias", @@ -187,22 +161,14 @@ export function VirtualKeysTable() { cell: ({ row }) => { const key = row.original; if (key.blocked !== true) { - return ( - - Active - - ); + return ; } const isScimBlocked = (key.metadata as Record | null | undefined)?.scim_blocked === true; const reason = isScimBlocked ? "Blocked by SCIM (external identity provider deactivated or deleted the owning user)." : "Blocked. Requests using this key will be rejected with 401."; return ( - - - Blocked - - + ); }, }, @@ -323,10 +289,7 @@ export function VirtualKeysTable() { header: "Created At", size: 120, enableSorting: true, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleDateString() : "-"; - }, + cell: (info) => , }, { id: "created_by", @@ -394,10 +357,7 @@ export function VirtualKeysTable() { header: "Updated At", size: 120, enableSorting: true, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleDateString() : "Never"; - }, + cell: (info) => , }, { id: "last_active", @@ -415,16 +375,7 @@ export function VirtualKeysTable() { ), size: 130, enableSorting: false, - cell: (info) => { - const value = info.getValue(); - if (!value) return "Unknown"; - const date = new Date(value as string); - return ( - - {date.toLocaleDateString()} - - ); - }, + cell: (info) => , }, { id: "expires", @@ -432,10 +383,7 @@ export function VirtualKeysTable() { header: "Expires", size: 120, enableSorting: false, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleDateString() : "Never"; - }, + cell: (info) => , }, { id: "spend", @@ -443,7 +391,7 @@ export function VirtualKeysTable() { header: "Spend (USD)", size: 100, enableSorting: true, - cell: (info) => formatNumberWithCommas(info.getValue() as number, 4), + cell: (info) => , }, { id: "max_budget", @@ -470,10 +418,7 @@ export function VirtualKeysTable() { header: "Budget Reset", size: 130, enableSorting: false, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleString() : "Never"; - }, + cell: (info) => , }, { id: "models", diff --git a/ui/litellm-dashboard/src/components/agents.tsx b/ui/litellm-dashboard/src/components/agents.tsx index 0d8916942da..e3703f6c588 100644 --- a/ui/litellm-dashboard/src/components/agents.tsx +++ b/ui/litellm-dashboard/src/components/agents.tsx @@ -20,7 +20,7 @@ import AgentInfoView from "./agents/agent_info"; import NotificationsManager from "./molecules/notifications_manager"; import { Agent } from "./agents/types"; import { Team } from "./key_team_helpers/key_list"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { DateCell, IdCell, MoneyCell, StatusBadge } from "@/components/shared/table_cells"; import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; interface AgentsPanelProps { @@ -193,19 +193,10 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams {agent.agent_name} - - - + setSelectedAgentId(id)} /> - {formatNumberWithCommas(agent.spend, 4)} + @@ -213,13 +204,13 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams - {agent.created_at ? new Date(agent.created_at).toLocaleDateString() : "N/A"} + {(agent.keys?.length ?? 0) > 0 ? ( - Active + ) : ( - Needs Setup + )} {isAdmin && ( diff --git a/ui/litellm-dashboard/src/components/claude_code_plugins/plugin_table.tsx b/ui/litellm-dashboard/src/components/claude_code_plugins/plugin_table.tsx index 1646161891f..0932e658899 100644 --- a/ui/litellm-dashboard/src/components/claude_code_plugins/plugin_table.tsx +++ b/ui/litellm-dashboard/src/components/claude_code_plugins/plugin_table.tsx @@ -11,6 +11,7 @@ import { import { Badge, Button, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "@tremor/react"; import { Tooltip } from "antd"; import React, { useState } from "react"; +import { DateCell, IdCell, StatusBadge } from "@/components/shared/table_cells"; import NotificationsManager from "../molecules/notifications_manager"; import { getCategoryBadgeColor } from "./helpers"; import { Plugin } from "./types"; @@ -34,12 +35,6 @@ const PluginTable: React.FC = ({ }) => { const [sorting, setSorting] = useState([{ id: "created_at", desc: true }]); - const formatDate = (dateString?: string) => { - if (!dateString) return "-"; - const date = new Date(dateString); - return date.toLocaleString(); - }; - const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); NotificationsManager.success("Copied to clipboard!"); @@ -51,19 +46,9 @@ const PluginTable: React.FC = ({ accessorKey: "name", cell: ({ row }) => { const plugin = row.original; - const name = plugin.name || ""; return (
- - - + onPluginClick(plugin.id)} /> { @@ -122,24 +107,13 @@ const PluginTable: React.FC = ({ accessorKey: "enabled", cell: ({ row }) => { const plugin = row.original; - return ( - - {plugin.enabled ? "Yes" : "No"} - - ); + return ; }, }, { header: "Created At", accessorKey: "created_at", - cell: ({ row }) => { - const plugin = row.original; - return ( - - {formatDate(plugin.created_at)} - - ); - }, + cell: ({ row }) => , }, ...(isAdmin ? [ diff --git a/ui/litellm-dashboard/src/components/general_settings.tsx b/ui/litellm-dashboard/src/components/general_settings.tsx index 5b8dec39505..038547c6e0e 100644 --- a/ui/litellm-dashboard/src/components/general_settings.tsx +++ b/ui/litellm-dashboard/src/components/general_settings.tsx @@ -4,7 +4,6 @@ import { Table, TableHead, TableRow, - Badge, TableHeaderCell, TableCell, TableBody, @@ -16,7 +15,8 @@ import { import { TabPanel, TabPanels, TabGroup, TabList, Tab } from "@tremor/react"; import { getGeneralSettingsCall, updateConfigFieldSetting, deleteConfigFieldSetting } from "./networking"; import { InputNumber } from "antd"; -import { TrashIcon, CheckCircleIcon } from "@heroicons/react/outline"; +import { TrashIcon } from "@heroicons/react/outline"; +import { StatusBadge } from "@/components/shared/table_cells"; import RouterSettings from "./router_settings"; import Fallbacks from "./Settings/RouterSettings/Fallbacks/Fallbacks"; @@ -173,13 +173,11 @@ const GeneralSettings: React.FC = ({ accessToken, user {value.stored_in_db == true ? ( - - In DB - + ) : value.stored_in_db == false ? ( - In Config + ) : ( - Not Set + )} diff --git a/ui/litellm-dashboard/src/components/guardrails/guardrail_table.tsx b/ui/litellm-dashboard/src/components/guardrails/guardrail_table.tsx index ecf6ce48fde..99f6b2793fd 100644 --- a/ui/litellm-dashboard/src/components/guardrails/guardrail_table.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/guardrail_table.tsx @@ -1,8 +1,8 @@ import React, { useState } from "react"; -import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Icon, Button } from "@tremor/react"; +import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Icon } from "@tremor/react"; import { TrashIcon, SwitchVerticalIcon, ChevronUpIcon, ChevronDownIcon } from "@heroicons/react/outline"; import { Tooltip } from "antd"; -import { Badge } from "@tremor/react"; +import { DateCell, IdCell, StatusBadge } from "@/components/shared/table_cells"; import { ColumnDef, flexRender, @@ -43,13 +43,6 @@ const GuardrailTable: React.FC = ({ const [editModalVisible, setEditModalVisible] = useState(false); const [selectedGuardrail, setSelectedGuardrail] = useState(null); - // Format date helper function - const formatDate = (dateString?: string) => { - if (!dateString) return "-"; - const date = new Date(dateString); - return date.toLocaleString(); - }; - const handleEditClick = (guardrail: Guardrail) => { setSelectedGuardrail(guardrail); setEditModalVisible(true); @@ -65,18 +58,7 @@ const GuardrailTable: React.FC = ({ { header: "Guardrail ID", accessorKey: "guardrail_id", - cell: (info: any) => ( - - - - ), + cell: (info: any) => , }, { header: "Name", @@ -126,41 +108,21 @@ const GuardrailTable: React.FC = ({ header: "Default On", accessorKey: "litellm_params.default_on", cell: ({ row }) => { - const guardrail = row.original; + const isDefaultOn = !!row.original.litellm_params?.default_on; return ( - - {guardrail.litellm_params?.default_on ? "Default On" : "Default Off"} - + ); }, }, { header: "Created At", accessorKey: "created_at", - cell: ({ row }) => { - const guardrail = row.original; - return ( - - {formatDate(guardrail.created_at)} - - ); - }, + cell: ({ row }) => , }, { header: "Updated At", accessorKey: "updated_at", - cell: ({ row }) => { - const guardrail = row.original; - return ( - - {formatDate(guardrail.updated_at)} - - ); - }, + cell: ({ row }) => , }, { id: "actions", diff --git a/ui/litellm-dashboard/src/components/mcp_hub_table_columns.tsx b/ui/litellm-dashboard/src/components/mcp_hub_table_columns.tsx index 7cf0d48a49f..1e25f87d262 100644 --- a/ui/litellm-dashboard/src/components/mcp_hub_table_columns.tsx +++ b/ui/litellm-dashboard/src/components/mcp_hub_table_columns.tsx @@ -2,6 +2,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { Button, Badge, Text } from "@tremor/react"; import { Tooltip, Tag } from "antd"; import { CopyOutlined, InfoCircleOutlined } from "@ant-design/icons"; +import { StatusBadge, type StatusTone } from "@/components/shared/table_cells"; export interface MCPServerData { server_id: string; @@ -124,21 +125,17 @@ export const mcpHubColumns = ( cell: ({ row }) => { const server = row.original; - const statusColors: Record = { - active: "green", - inactive: "red", - unknown: "gray", - healthy: "green", - unhealthy: "red", + const statusTones: Record = { + active: "success", + inactive: "error", + unknown: "neutral", + healthy: "success", + unhealthy: "error", }; - const color = statusColors[server.status] || "gray"; + const tone = statusTones[server.status] || "neutral"; - return ( - - {server.status || "unknown"} - - ); + return ; }, }, { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx b/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx index 546df9ebc4b..5e7e99ee8ec 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/MCPToolsetsTab.tsx @@ -6,6 +6,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { useMCPToolsets } from "@/app/(dashboard)/hooks/mcpServers/useMCPToolsets"; import { useMCPServers } from "@/app/(dashboard)/hooks/mcpServers/useMCPServers"; import { useQueryClient } from "@tanstack/react-query"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { DataTable } from "../view_logs/table"; import { createMCPToolset, updateMCPToolset, deleteMCPToolset, listMCPTools, getProxyBaseUrl } from "../networking"; import { MCPToolset, MCPToolsetTool } from "./types"; @@ -302,11 +303,7 @@ function toolsetColumns( { header: "Toolset ID", accessorKey: "toolset_id", - cell: ({ row }) => ( - - {row.original.toolset_id.slice(0, 8)}… - - ), + cell: ({ row }) => , }, { header: "Name", @@ -359,11 +356,7 @@ function toolsetColumns( { header: "Created", accessorKey: "created_at", - cell: ({ row }) => ( - - {row.original.created_at ? new Date(row.original.created_at).toLocaleDateString() : "—"} - - ), + cell: ({ row }) => , }, ...(isAdmin ? [ diff --git a/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.tsx b/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.tsx index a2194e23b6e..6497f2686cb 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from "react"; -import { Title, Text, Button, Badge } from "@tremor/react"; +import { Title, Text, Button } from "@tremor/react"; import { Modal } from "antd"; import { Button as AntdButton } from "antd"; import { ModelDataTable } from "./table"; @@ -468,21 +468,6 @@ const HealthCheckComponent: React.FC = ({ onPageChange?.(page); }; - const getStatusBadge = (status: string) => { - switch (status) { - case "healthy": - return healthy; - case "unhealthy": - return unhealthy; - case "checking": - return checking; - case "none": - return none; - default: - return unknown; - } - }; - const showErrorModal = (modelName: string, cleanedError: string, fullError: string) => { setSelectedErrorDetails({ modelName, @@ -612,7 +597,6 @@ const HealthCheckComponent: React.FC = ({ handleModelSelection, handleSelectAll, runIndividualHealthCheck, - getStatusBadge, getDisplayModelName, showErrorModal, showSuccessModal, diff --git a/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx b/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx index 12cc984ef91..33c97236f97 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx @@ -3,6 +3,7 @@ import { Tooltip, Checkbox } from "antd"; import { Text } from "@tremor/react"; import { InformationCircleIcon, PlayIcon, RefreshIcon } from "@heroicons/react/outline"; import { Team } from "@/components/key_team_helpers/key_list"; +import { IdCell, StatusBadge, type StatusTone } from "@/components/shared/table_cells"; interface HealthCheckData { model_name: string; @@ -21,6 +22,18 @@ interface HealthCheckData { health_full_error?: string; } +const HEALTH_STATUS_TONES: Record = { + healthy: "success", + unhealthy: "error", + checking: "info", + none: "neutral", +}; + +const healthStatusBadge = (status: string): JSX.Element => { + const tone = HEALTH_STATUS_TONES[status]; + return tone ? : ; +}; + interface HealthStatus { status: string; lastCheck: string; @@ -38,7 +51,6 @@ export const healthCheckColumns = ( handleModelSelection: (modelId: string, checked: boolean) => void, handleSelectAll: (checked: boolean) => void, runIndividualHealthCheck: (modelId: string) => void, - getStatusBadge: (status: string) => JSX.Element, getDisplayModelName: (model: any) => string, showErrorModal?: (modelName: string, cleanedError: string, fullError: string) => void, showSuccessModal?: (modelName: string, response: any) => void, @@ -72,14 +84,7 @@ export const healthCheckColumns = ( onChange={(e) => handleModelSelection(modelId, e.target.checked)} onClick={(e) => e.stopPropagation()} /> - -
setSelectedModelId && setSelectedModelId(model.model_info.id)} - > - {model.model_info.id} -
-
+
); }, @@ -175,7 +180,7 @@ export const healthCheckColumns = ( return (
- {getStatusBadge(healthStatus.status)} + {healthStatusBadge(healthStatus.status)} {hasSuccessResponse && showSuccessModal && ( - +
e.stopPropagation()}> +
) : ( "-" @@ -370,15 +345,10 @@ export const columns = ( minSize: 80, cell: ({ row }) => { const model = row.original; - return ( -
- {model.model_info.db_model ? "DB Model" : "Config Model"} -
+ return model.model_info.db_model ? ( + + ) : ( + ); }, }, diff --git a/ui/litellm-dashboard/src/components/organization/organization_view.tsx b/ui/litellm-dashboard/src/components/organization/organization_view.tsx index e1b8c6d5044..402cc33902f 100644 --- a/ui/litellm-dashboard/src/components/organization/organization_view.tsx +++ b/ui/litellm-dashboard/src/components/organization/organization_view.tsx @@ -1,6 +1,7 @@ import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; import { organizationKeys, useOrganization } from "@/app/(dashboard)/hooks/organizations/useOrganizations"; import { useQueryClient } from "@tanstack/react-query"; +import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils"; import { createTeamAliasMap } from "@/utils/teamUtils"; import { ArrowLeftIcon } from "@heroicons/react/outline"; @@ -196,7 +197,7 @@ const OrganizationInfoView: React.FC = ({ render: (_: unknown, record: Member) => { const orgMember = record.user_id != null ? (orgData.members || []).find((m) => m.user_id === record.user_id) : undefined; - return ${formatNumberWithCommas(orgMember?.spend ?? 0, 4)}; + return ; }, }, { diff --git a/ui/litellm-dashboard/src/components/organizations.tsx b/ui/litellm-dashboard/src/components/organizations.tsx index 857a0a0c64c..edebc17087a 100644 --- a/ui/litellm-dashboard/src/components/organizations.tsx +++ b/ui/litellm-dashboard/src/components/organizations.tsx @@ -27,7 +27,7 @@ import { import { Form, Input, Modal, Select as Select2, Tooltip } from "antd"; import { useQueryClient } from "@tanstack/react-query"; import React, { useState } from "react"; -import { formatNumberWithCommas } from "../utils/dataUtils"; +import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; import DeleteResourceModal from "./common_components/DeleteResourceModal"; import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_team_key"; @@ -263,30 +263,22 @@ const OrganizationsTable: React.FC = ({ .map((org: Organization) => ( -
- - - -
+
{org.organization_alias} - {org.created_at ? new Date(org.created_at).toLocaleDateString() : "N/A"} + - {formatNumberWithCommas(org.spend, 4)} - {org.litellm_budget_table?.max_budget !== null && - org.litellm_budget_table?.max_budget !== undefined - ? org.litellm_budget_table?.max_budget - : "No limit"} + + + + = ({ { header: "ID", accessorKey: "id", - cell: (info: any) => ( - -
info.row.original.id && setSelectedEndpointId(info.row.original.id)} - > - {info.row.original.id} -
-
- ), + cell: (info: any) => , }, { header: "Path", @@ -192,7 +184,9 @@ const PassThroughSettings: React.FC = ({
), accessorKey: "auth", - cell: (info: any) => {info.getValue() ? "Yes" : "No"}, + cell: (info: any) => ( + + ), }, { header: "Headers", diff --git a/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx b/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx index 099aa97a433..0cfd4e41e0a 100644 --- a/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx +++ b/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx @@ -140,10 +140,13 @@ describe("AttachmentTable", () => { expect(screen.queryByRole("button", { name: /TrashIcon/i })).not.toBeInTheDocument(); }); - it("should show a truncated attachment ID in the table", () => { + it("should show the attachment ID as truncated plain mono text", () => { const attachment = makeAttachment({ attachment_id: "att-abcdef1234567" }); renderWithProviders(); - expect(screen.getByText("att-abc...")).toBeInTheDocument(); + const idElement = screen.getByText("att-abcdef1234567"); + expect(idElement.className).toContain("font-mono"); + expect(idElement.className).toContain("truncate"); + expect(idElement.className).not.toContain("bg-blue-50"); }); it("should render model tags when the attachment has models", () => { diff --git a/ui/litellm-dashboard/src/components/policies/attachment_table.tsx b/ui/litellm-dashboard/src/components/policies/attachment_table.tsx index d9de8378a8a..fa482552fd5 100644 --- a/ui/litellm-dashboard/src/components/policies/attachment_table.tsx +++ b/ui/litellm-dashboard/src/components/policies/attachment_table.tsx @@ -10,6 +10,7 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { PolicyAttachment } from "./types"; import ImpactPopover from "./impact_popover"; @@ -30,24 +31,11 @@ const AttachmentTable: React.FC = ({ }) => { const [sorting, setSorting] = useState([{ id: "created_at", desc: true }]); - // Format date helper function - const formatDate = (dateString?: string) => { - if (!dateString) return "-"; - const date = new Date(dateString); - return date.toLocaleString(); - }; - const columns: ColumnDef[] = [ { header: "Attachment ID", accessorKey: "attachment_id", - cell: (info: any) => ( - - - {info.getValue() ? `${String(info.getValue()).slice(0, 7)}...` : ""} - - - ), + cell: (info: any) => , }, { header: "Policy", @@ -183,14 +171,7 @@ const AttachmentTable: React.FC = ({ { header: "Created At", accessorKey: "created_at", - cell: ({ row }) => { - const attachment = row.original; - return ( - - {formatDate(attachment.created_at)} - - ); - }, + cell: ({ row }) => , }, { id: "actions", diff --git a/ui/litellm-dashboard/src/components/policies/policy_table.tsx b/ui/litellm-dashboard/src/components/policies/policy_table.tsx index eafec442474..1716f5aea84 100644 --- a/ui/litellm-dashboard/src/components/policies/policy_table.tsx +++ b/ui/litellm-dashboard/src/components/policies/policy_table.tsx @@ -10,6 +10,7 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; +import { DateCell } from "@/components/shared/table_cells"; import { Policy } from "./types"; /** One row per policy name; primaryPolicy is used for display and for Edit (FlowBuilder loads all versions) */ @@ -59,12 +60,6 @@ const PolicyTable: React.FC = ({ const rows = useMemo(() => groupPoliciesByName(policies), [policies]); - const formatDate = (dateString?: string) => { - if (!dateString) return "-"; - const date = new Date(dateString); - return date.toLocaleString(); - }; - const columns: ColumnDef[] = [ { header: "Name", @@ -199,14 +194,7 @@ const PolicyTable: React.FC = ({ header: "Created At", id: "created_at", accessorFn: (row) => row.primaryPolicy.created_at ?? "", - cell: ({ row }) => { - const policy = row.original.primaryPolicy; - return ( - - {formatDate(policy.created_at)} - - ); - }, + cell: ({ row }) => , }, { id: "actions", diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/cell_tooltip.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/cell_tooltip.tsx new file mode 100644 index 00000000000..c6e10590e8f --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/cell_tooltip.tsx @@ -0,0 +1,21 @@ +"use client"; + +import * as React from "react"; + +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; + +interface CellTooltipProps { + content: React.ReactNode; + trigger: React.ReactElement; +} + +export function CellTooltip({ content, trigger }: CellTooltipProps) { + return ( + + + + {content} + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/date_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/date_cell.test.tsx new file mode 100644 index 00000000000..719047e0cfe --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/date_cell.test.tsx @@ -0,0 +1,57 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { DateCell, formatCellDate, formatFullTimestamp } from "./date_cell"; + +const localIso = new Date(2026, 6, 7, 9, 50, 13).toISOString(); + +describe("formatCellDate", () => { + it("formats datetime precision as 'MMM D, HH:mm:ss' without a year", () => { + expect(formatCellDate(new Date(2026, 6, 7, 9, 50, 13), "datetime")).toBe("Jul 7, 09:50:13"); + }); + + it("zero-pads hours, minutes and seconds", () => { + expect(formatCellDate(new Date(2026, 0, 2, 1, 2, 3), "datetime")).toBe("Jan 2, 01:02:03"); + }); + + it("formats date precision as 'MMM D, YYYY' with no time", () => { + expect(formatCellDate(new Date(2026, 11, 31, 23, 59, 59), "date")).toBe("Dec 31, 2026"); + }); +}); + +describe("formatFullTimestamp", () => { + it("includes year, 24h time and the IANA timezone", () => { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + expect(formatFullTimestamp(new Date(2026, 6, 7, 9, 50, 13))).toBe(`Jul 7, 2026, 09:50:13 (${timeZone})`); + }); +}); + +describe("DateCell", () => { + it("renders the datetime format by default", () => { + render(); + expect(screen.getByText("Jul 7, 09:50:13")).toBeInTheDocument(); + }); + + it("renders date-only when precision is 'date'", () => { + render(); + expect(screen.getByText("Jul 7, 2026")).toBeInTheDocument(); + }); + + it("renders '-' for null and undefined", () => { + const { rerender } = render(); + expect(screen.getByText("-")).toBeInTheDocument(); + rerender(); + expect(screen.getByText("-")).toBeInTheDocument(); + }); + + it("renders the custom fallback for empty values", () => { + render(); + expect(screen.getByText("Never")).toBeInTheDocument(); + }); + + it("renders the fallback instead of 'Invalid Date' for unparseable input", () => { + render(); + expect(screen.getByText("Unknown")).toBeInTheDocument(); + expect(screen.queryByText(/Invalid/)).not.toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/date_cell.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/date_cell.tsx new file mode 100644 index 00000000000..ee4c01bb237 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/date_cell.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { CellTooltip } from "./cell_tooltip"; + +const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] as const; + +export type DatePrecision = "datetime" | "date"; + +interface DateCellProps { + value: string | null | undefined; + precision?: DatePrecision; + fallback?: string; +} + +const pad = (n: number): string => String(n).padStart(2, "0"); + +export const formatCellDate = (date: Date, precision: DatePrecision): string => + precision === "date" + ? `${MONTHS[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}` + : `${MONTHS[date.getMonth()]} ${date.getDate()}, ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; + +export const formatFullTimestamp = (date: Date): string => { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const day = `${MONTHS[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`; + const time = `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; + return `${day}, ${time} (${timeZone})`; +}; + +export function DateCell({ value, precision = "datetime", fallback = "-" }: DateCellProps) { + const date = value ? new Date(value) : null; + if (!date || Number.isNaN(date.getTime())) { + return {fallback}; + } + + return ( + {formatCellDate(date, precision)}} + /> + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx new file mode 100644 index 00000000000..1a87f17d50b --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx @@ -0,0 +1,78 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; + +import { IdCell } from "./id_cell"; + +const { copyToClipboardMock } = vi.hoisted(() => ({ copyToClipboardMock: vi.fn() })); + +vi.mock("@/utils/dataUtils", async (importOriginal) => ({ + ...(await importOriginal()), + copyToClipboard: copyToClipboardMock, +})); + +describe("IdCell", () => { + it("renders '-' for empty values", () => { + render(); + expect(screen.getByText("-")).toBeInTheDocument(); + }); + + it("renders the custom fallback for empty values", () => { + render(); + expect(screen.getByText("—")).toBeInTheDocument(); + }); + + it("renders the full id as a non-interactive pill by default", () => { + render(); + const el = screen.getByText("sk-1234567890abcdef"); + expect(el.tagName).toBe("SPAN"); + expect(el.className).toContain("bg-blue-50"); + expect(el.className).toContain("font-mono"); + expect(el.className).toContain("max-w-[15ch]"); + expect(el.className).toContain("truncate"); + }); + + it("renders plain mono text without pill styling for the plain variant", () => { + render(); + const el = screen.getByText("req-123"); + expect(el.className).toContain("font-mono"); + expect(el.className).not.toContain("bg-blue-50"); + }); + + it("does not truncate when truncate is false", () => { + render(); + expect(screen.getByText("audit-object-id").className).not.toContain("truncate"); + }); + + it("becomes a button that fires onClick with the id value", async () => { + const user = userEvent.setup(); + const onClick = vi.fn(); + render(); + await user.click(screen.getByRole("button", { name: "team-42" })); + expect(onClick).toHaveBeenCalledWith("team-42"); + }); + + it("stays non-interactive when disabled, even with onClick", () => { + const onClick = vi.fn(); + render(); + expect(screen.queryByRole("button", { name: "tag-1" })).not.toBeInTheDocument(); + }); + + it("copies the id via the trailing copy button without triggering row clicks", async () => { + const user = userEvent.setup(); + const rowClick = vi.fn(); + render( +
+ +
, + ); + await user.click(screen.getByRole("button", { name: "Copy ID" })); + expect(copyToClipboardMock).toHaveBeenCalledWith("key-hash-9"); + expect(rowClick).not.toHaveBeenCalled(); + }); + + it("passes dataTestId through to the id element", () => { + render(); + expect(screen.getByTestId("key-id-cell")).toHaveTextContent("k-1"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.tsx new file mode 100644 index 00000000000..6fbd2e2f9ed --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.tsx @@ -0,0 +1,94 @@ +"use client"; + +import { Copy } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/cva.config"; +import { copyToClipboard } from "@/utils/dataUtils"; + +import { CellTooltip } from "./cell_tooltip"; + +export type IdCellVariant = "pill" | "plain"; + +interface IdCellProps { + value: string | null | undefined; + variant?: IdCellVariant; + onClick?: (value: string) => void; + copyable?: boolean; + truncate?: boolean; + fallback?: string; + tooltip?: React.ReactNode; + disabled?: boolean; + dataTestId?: string; + className?: string; +} + +const VARIANT_CLASS: Record = { + pill: { + base: "font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500", + clickable: "hover:bg-blue-100 cursor-pointer", + }, + plain: { + base: "font-mono text-xs text-left", + clickable: "hover:text-blue-600 cursor-pointer", + }, +}; + +export function IdCell({ + value, + variant = "pill", + onClick, + copyable = false, + truncate = true, + fallback = "-", + tooltip, + disabled = false, + dataTestId, + className, +}: IdCellProps) { + if (!value) { + return {fallback}; + } + + const clickable = !!onClick && !disabled; + const classes = cn( + VARIANT_CLASS[variant].base, + clickable && VARIANT_CLASS[variant].clickable, + truncate && "block max-w-[15ch] truncate", + disabled && "opacity-50", + className, + ); + + const idElement = clickable ? ( + + ) : ( + + {value} + + ); + + const withTooltip = ; + + if (!copyable) { + return withTooltip; + } + + return ( + + {withTooltip} + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/index.ts b/ui/litellm-dashboard/src/components/shared/table_cells/index.ts new file mode 100644 index 00000000000..e189413d43d --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/index.ts @@ -0,0 +1,5 @@ +export { CellTooltip } from "./cell_tooltip"; +export { DateCell, formatCellDate, formatFullTimestamp, type DatePrecision } from "./date_cell"; +export { IdCell, type IdCellVariant } from "./id_cell"; +export { MoneyCell } from "./money_cell"; +export { StatusBadge, type StatusTone } from "./status_badge"; diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/money_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/money_cell.test.tsx new file mode 100644 index 00000000000..473785e31c4 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/money_cell.test.tsx @@ -0,0 +1,44 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { MoneyCell } from "./money_cell"; + +describe("MoneyCell", () => { + it("renders '-' for null and undefined", () => { + const { rerender } = render(); + expect(screen.getByText("-")).toBeInTheDocument(); + rerender(); + expect(screen.getByText("-")).toBeInTheDocument(); + }); + + it("renders the custom emptyText for null budgets", () => { + render(); + expect(screen.getByText("Unlimited")).toBeInTheDocument(); + }); + + it("renders '-' for zero by default", () => { + render(); + expect(screen.getByText("-")).toBeInTheDocument(); + }); + + it("renders a formatted zero when showZero is set, never the emptyText", () => { + render(); + expect(screen.getByText("$0.00")).toBeInTheDocument(); + expect(screen.queryByText("Unlimited")).not.toBeInTheDocument(); + }); + + it("formats amounts with commas, a dollar sign and the given decimals", () => { + render(); + expect(screen.getByText("$1,234.57")).toBeInTheDocument(); + }); + + it("defaults to 4 decimals", () => { + render(); + expect(screen.getByText("$42.0000")).toBeInTheDocument(); + }); + + it("renders the sub-threshold form for amounts that round to zero", () => { + render(); + expect(screen.getByText("< $0.000001")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/money_cell.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/money_cell.tsx new file mode 100644 index 00000000000..9d3c747b20e --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/money_cell.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { formatNumberWithCommas, getSpendString } from "@/utils/dataUtils"; + +interface MoneyCellProps { + value: number | null | undefined; + decimals?: number; + emptyText?: string; + showZero?: boolean; +} + +export function MoneyCell({ value, decimals = 4, emptyText = "-", showZero = false }: MoneyCellProps) { + if (value === null || value === undefined || Number.isNaN(value)) { + return {emptyText}; + } + if (value === 0) { + if (!showZero) { + return -; + } + return {`$${formatNumberWithCommas(0, decimals, false, true)}`}; + } + return {getSpendString(value, decimals)}; +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/status_badge.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/status_badge.test.tsx new file mode 100644 index 00000000000..724a362c21f --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/status_badge.test.tsx @@ -0,0 +1,42 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it } from "vitest"; + +import { StatusBadge, type StatusTone } from "./status_badge"; + +describe("StatusBadge", () => { + const toneClasses: Record = { + success: ["border-green-200", "bg-green-50", "text-green-600"], + error: ["border-red-200", "bg-red-50", "text-red-600"], + warning: ["border-amber-200", "bg-amber-50", "text-amber-600"], + neutral: ["border-gray-200", "bg-gray-50", "text-gray-600"], + info: ["border-blue-200", "bg-blue-50", "text-blue-600"], + }; + + (Object.entries(toneClasses) as [StatusTone, string[]][]).forEach(([tone, classes]) => { + it(`renders a tinted pill (${classes.join(" ")}) for the ${tone} tone`, () => { + render(); + const badge = screen.getByText(tone); + classes.forEach((cls) => expect(badge.className).toContain(cls)); + }); + }); + + it("renders the label text inside an outline badge with no status dot", () => { + render(); + const badge = screen.getByText("Active"); + expect(badge.dataset.variant).toBe("outline"); + expect(badge.querySelector("[aria-hidden]")).toBeNull(); + }); + + it("passes dataTestId through", () => { + render(); + expect(screen.getByTestId("key-status")).toHaveTextContent("Blocked"); + }); + + it("opens the tooltip on hover, which requires Badge to forward its ref to the trigger", async () => { + const user = userEvent.setup(); + render(); + await user.hover(screen.getByText("Blocked")); + expect(await screen.findByText("This key was blocked by SCIM")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/status_badge.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/status_badge.tsx new file mode 100644 index 00000000000..f10f817650a --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/status_badge.tsx @@ -0,0 +1,38 @@ +"use client"; + +import * as React from "react"; + +import { Badge } from "@/components/ui/badge"; +import { cn } from "@/lib/cva.config"; + +import { CellTooltip } from "./cell_tooltip"; + +export type StatusTone = "success" | "error" | "warning" | "neutral" | "info"; + +const TONE_CLASS: Record = { + success: "border-green-200 bg-green-50 text-green-600", + error: "border-red-200 bg-red-50 text-red-600", + warning: "border-amber-200 bg-amber-50 text-amber-600", + neutral: "border-gray-200 bg-gray-50 text-gray-600", + info: "border-blue-200 bg-blue-50 text-blue-600", +}; + +interface StatusBadgeProps { + tone: StatusTone; + label: string; + tooltip?: React.ReactNode; + dataTestId?: string; +} + +export function StatusBadge({ tone, label, tooltip, dataTestId }: StatusBadgeProps) { + const badge = ( + + {label} + + ); + + if (!tooltip) { + return badge; + } + return ; +} diff --git a/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx b/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx index 8a8d7ae7042..8fc9adc75a2 100644 --- a/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx +++ b/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx @@ -3,6 +3,7 @@ import { Badge, Text } from "@tremor/react"; import { Tooltip } from "antd"; import { CopyOutlined, LinkOutlined } from "@ant-design/icons"; import { Plugin } from "./claude_code_plugins/types"; +import { StatusBadge } from "@/components/shared/table_cells"; export const skillHubColumns = ( showModal: (skill: Plugin) => void, @@ -104,9 +105,10 @@ export const skillHubColumns = ( accessorKey: "enabled", enableSorting: true, cell: ({ row }) => ( - - {row.original.enabled ? "Public" : "Draft"} - + ), }, ]; diff --git a/ui/litellm-dashboard/src/components/tag_management/TagTable.test.tsx b/ui/litellm-dashboard/src/components/tag_management/TagTable.test.tsx index a56721787d5..057f30ccee5 100644 --- a/ui/litellm-dashboard/src/components/tag_management/TagTable.test.tsx +++ b/ui/litellm-dashboard/src/components/tag_management/TagTable.test.tsx @@ -1,5 +1,6 @@ -import { render, screen } from "@testing-library/react"; +import { fireEvent, render, screen } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { formatCellDate } from "@/components/shared/table_cells"; import TagTable from "./TagTable"; import { Tag } from "./types"; @@ -75,14 +76,21 @@ describe("TagTable", () => { it("should display formatted created date", () => { render(); - const formattedDate = new Date(mockTag.created_at).toLocaleDateString(); + const formattedDate = formatCellDate(new Date(mockTag.created_at), "date"); expect(screen.getByText(formattedDate)).toBeInTheDocument(); }); - it("should disable tag name button for dynamic spend tags", () => { + it("should call onSelectTag when tag name is clicked", () => { + render(); + fireEvent.click(screen.getByRole("button", { name: "test-tag" })); + expect(mockOnSelectTag).toHaveBeenCalledWith("test-tag"); + }); + + it("should render tag name as non-clickable for dynamic spend tags", () => { render(); - const tagButton = screen.getByRole("button", { name: "dynamic-spend-tag" }); - expect(tagButton).toBeDisabled(); + expect(screen.queryByRole("button", { name: "dynamic-spend-tag" })).not.toBeInTheDocument(); + fireEvent.click(screen.getByText("dynamic-spend-tag")); + expect(mockOnSelectTag).not.toHaveBeenCalled(); }); it("should disable edit icon for dynamic spend tags", () => { diff --git a/ui/litellm-dashboard/src/components/tag_management/TagTable.tsx b/ui/litellm-dashboard/src/components/tag_management/TagTable.tsx index ce28ac6e6f2..e34653cc702 100644 --- a/ui/litellm-dashboard/src/components/tag_management/TagTable.tsx +++ b/ui/litellm-dashboard/src/components/tag_management/TagTable.tsx @@ -7,20 +7,10 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; -import { - Badge, - Button, - Icon, - Table, - TableBody, - TableCell, - TableHead, - TableHeaderCell, - TableRow, - Text, -} from "@tremor/react"; +import { Badge, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react"; import { Tooltip } from "antd"; import React from "react"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { Tag } from "./types"; interface TagTableProps { @@ -45,21 +35,15 @@ const TagTable: React.FC = ({ data, onEdit, onDelete, onSelectTag const isDynamicSpendTag = tag.description === DYNAMIC_SPEND_TAG_DESCRIPTION; return (
- - - + />
); }, @@ -104,10 +88,7 @@ const TagTable: React.FC = ({ data, onEdit, onDelete, onSelectTag header: "Created", accessorKey: "created_at", sortingFn: "datetime", - cell: ({ row }) => { - const tag = row.original; - return {new Date(tag.created_at).toLocaleDateString()}; - }, + cell: ({ row }) => , }, { id: "actions", diff --git a/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx b/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx index ac0ae16a44f..a07c57eaa30 100644 --- a/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx @@ -27,6 +27,8 @@ const mockSetSelectedEditMember = vi.fn(); const mockSetIsEditMemberModalVisible = vi.fn(); const mockSetIsAddMemberModalVisible = vi.fn(); +const budgetResetIso = new Date(2026, 6, 15, 12, 0, 0).toISOString(); + const createMockTeamData = (overrides: Partial = {}): TeamData => ({ team_id: "team-123", team_info: { @@ -78,6 +80,7 @@ const createMockTeamData = (overrides: Partial = {}): TeamData => ({ rpm_limit: 100, model_max_budget: null, budget_duration: null, + budget_reset_at: budgetResetIso, }, }, ], @@ -202,7 +205,7 @@ describe("TeamMembersComponent", () => { />, ); - expect(screen.getByText("-")).toBeInTheDocument(); + expect(screen.getAllByText("-").length).toBeGreaterThanOrEqual(1); }); it("should display Default Proxy Admin tag for default_user_id", () => { @@ -243,12 +246,12 @@ describe("TeamMembersComponent", () => { />, ); - expect(screen.getByText(/\$100\.5/)).toBeInTheDocument(); + expect(screen.getByText("$100.5000")).toBeInTheDocument(); expect(screen.getByText(/100 RPM/)).toBeInTheDocument(); expect(screen.getByText(/10000 TPM/)).toBeInTheDocument(); }); - it("should display No Limit for budget when member has no budget", () => { + it("should display the budget reset date for member with a budget reset", () => { renderWithProviders( { />, ); - expect(screen.getByText("No Limit")).toBeInTheDocument(); + expect(screen.getByText("Jul 15, 2026")).toBeInTheDocument(); + }); + + it("should display formatted budget and Unlimited for member with no budget", () => { + renderWithProviders( + , + ); + + expect(screen.getByText("$1,000.0000")).toBeInTheDocument(); + expect(screen.getByText("Unlimited")).toBeInTheDocument(); }); it("should display No Limits for rate limits when member has no limits", () => { diff --git a/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx b/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx index e2f108dcbf5..b884490efc0 100644 --- a/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx @@ -1,7 +1,7 @@ import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { Member } from "@/components/networking"; -import { formatBudgetReset } from "@/utils/budgetUtils"; +import { DateCell, MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { isProxyAdminRole, isUserTeamAdminForSingleTeam } from "@/utils/roles"; import { InfoCircleOutlined } from "@ant-design/icons"; @@ -58,14 +58,10 @@ export default function TeamMemberTab({ return membership?.total_spend ?? 0; }; - const getUserBudget = (userId: string | null): string | null => { + const getUserBudget = (userId: string | null): number | null => { if (!userId) return null; const membership = teamData.team_memberships.find((tm) => tm.user_id === userId); - const maxBudget = membership?.litellm_budget_table?.max_budget; - if (maxBudget === null || maxBudget === undefined) { - return null; - } - return formatNumber(maxBudget); + return membership?.litellm_budget_table?.max_budget ?? null; }; // Helper function to get rate limits for a user @@ -98,7 +94,7 @@ export default function TeamMemberTab({ const getUserBudgetReset = (userId: string | null): string | null => { if (!userId) return null; const membership = teamData.team_memberships.find((tm) => tm.user_id === userId); - return formatBudgetReset(membership?.litellm_budget_table?.budget_reset_at); + return membership?.litellm_budget_table?.budget_reset_at ?? null; }; const extraColumns: ColumnsType = [ @@ -146,7 +142,7 @@ export default function TeamMemberTab({ ), key: "spend", render: (_: unknown, record: Member) => ( - ${formatNumberWithCommas(getUserCurrentCycleSpend(record.user_id), 4)} + ), }, { @@ -159,31 +155,19 @@ export default function TeamMemberTab({ ), key: "total_spend", - render: (_: unknown, record: Member) => ( - ${formatNumberWithCommas(getUserTotalSpend(record.user_id), 4)} - ), + render: (_: unknown, record: Member) => , }, { title: "Team Member Budget (USD)", key: "budget", - render: (_: unknown, record: Member) => { - const budget = getUserBudget(record.user_id); - return ( - {budget ? `$${formatNumberWithCommas(Number(budget), 4)}` : "No Limit"} - ); - }, + render: (_: unknown, record: Member) => ( + + ), }, { title: "Budget Reset", key: "budget_reset", - render: (_: unknown, record: Member) => { - const reset = getUserBudgetReset(record.user_id); - return reset ? ( - {reset} - ) : ( - - ); - }, + render: (_: unknown, record: Member) => , }, { title: ( diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index e51c124d618..b7128e642a5 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -2,7 +2,7 @@ "use client"; import { useKeys } from "@/app/(dashboard)/hooks/keys/useKeys"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline"; import { ColumnDef, @@ -12,18 +12,7 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; -import { - Badge, - Button, - Icon, - Table, - TableBody, - TableCell, - TableHead, - TableHeaderCell, - TableRow, - Text, -} from "@tremor/react"; +import { Badge, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react"; import { InfoCircleOutlined } from "@ant-design/icons"; import { Popover, Skeleton, Tooltip, Typography } from "antd"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; @@ -214,23 +203,9 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Key ID", size: 100, enableSorting: true, - cell: (info) => { - const value = info.getValue() as string; - const width = info.cell.column.getSize(); - return ( - - - - ); - }, + cell: (info) => ( + setSelectedKey(info.row.original)} /> + ), }, { id: "key_alias", @@ -310,10 +285,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Created At", size: 120, enableSorting: true, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleDateString() : "-"; - }, + cell: (info) => , }, { id: "created_by", @@ -380,10 +352,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Updated At", size: 120, enableSorting: true, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleDateString() : "Never"; - }, + cell: (info) => , }, { id: "last_active", @@ -401,16 +370,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi ), size: 130, enableSorting: false, - cell: (info) => { - const value = info.getValue(); - if (!value) return "Unknown"; - const date = new Date(value as string); - return ( - - {date.toLocaleDateString()} - - ); - }, + cell: (info) => , }, { id: "expires", @@ -418,10 +378,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Expires", size: 120, enableSorting: false, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleDateString() : "Never"; - }, + cell: (info) => , }, { id: "spend", @@ -429,7 +386,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Spend (USD)", size: 100, enableSorting: true, - cell: (info) => formatNumberWithCommas(info.getValue() as number, 4), + cell: (info) => , }, { id: "max_budget", @@ -437,11 +394,9 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Budget (USD)", size: 110, enableSorting: true, - cell: (info) => { - const maxBudget = info.getValue() as number | null; - if (maxBudget === null) return "Unlimited"; - return `$${formatNumberWithCommas(maxBudget)}`; - }, + cell: (info) => ( + + ), }, { id: "budget_reset_at", @@ -449,10 +404,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi header: "Budget Reset", size: 130, enableSorting: false, - cell: (info) => { - const value = info.getValue(); - return value ? new Date(value as string).toLocaleString() : "Never"; - }, + cell: (info) => , }, { id: "models", diff --git a/ui/litellm-dashboard/src/components/ui/badge.tsx b/ui/litellm-dashboard/src/components/ui/badge.tsx index 87b536cc37e..2e1ebffa109 100644 --- a/ui/litellm-dashboard/src/components/ui/badge.tsx +++ b/ui/litellm-dashboard/src/components/ui/badge.tsx @@ -21,14 +21,18 @@ const badgeVariants = cva({ }, }); -function Badge({ - className, - variant = "default", - ...props -}: React.ComponentProps<"span"> & VariantProps) { - return ( - - ); -} +const Badge = React.forwardRef< + HTMLSpanElement, + React.ComponentPropsWithoutRef<"span"> & VariantProps +>(({ className, variant = "default", ...props }, ref) => ( + +)); +Badge.displayName = "Badge"; export { Badge, badgeVariants }; diff --git a/ui/litellm-dashboard/src/components/usage.tsx b/ui/litellm-dashboard/src/components/usage.tsx index 04fcbddbd37..91c12fd1fa2 100644 --- a/ui/litellm-dashboard/src/components/usage.tsx +++ b/ui/litellm-dashboard/src/components/usage.tsx @@ -50,6 +50,7 @@ import { getProxyUISettings, } from "./networking"; import TopKeyView from "./UsagePage/components/EntityUsage/TopKeyView"; +import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; interface UsagePageProps { @@ -644,9 +645,7 @@ const UsagePage: React.FC = ({ accessToken, token, userRole, use {provider.provider} - {parseFloat(provider.spend.toFixed(2)) < 0.00001 - ? "less than 0.00" - : formatNumberWithCommas(provider.spend, 2)} + ))} @@ -819,7 +818,9 @@ const UsagePage: React.FC = ({ accessToken, token, userRole, use {topUsers?.map((user: any, index: number) => ( {user.end_user} - {formatNumberWithCommas(user.total_spend, 2)} + + + {user.total_count} ))} diff --git a/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx b/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx index c8288172d5b..eaa864ab3dc 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx @@ -1,7 +1,8 @@ import React from "react"; -import { Table, Badge, Tooltip } from "antd"; +import { Table, Tooltip } from "antd"; import MessageManager from "@/components/molecules/message_manager"; import { EyeOutlined, CopyOutlined, DeleteOutlined } from "@ant-design/icons"; +import { StatusBadge, type StatusTone } from "@/components/shared/table_cells"; import { DocumentUpload } from "./types"; interface DocumentsTableProps { @@ -16,15 +17,15 @@ const DocumentsTable: React.FC = ({ documents, onRemove }) }; const getStatusBadge = (status: DocumentUpload["status"]) => { - const statusConfig = { - uploading: { color: "blue", text: "Uploading" }, - done: { color: "green", text: "Ready" }, - error: { color: "red", text: "Error" }, - removed: { color: "default", text: "Removed" }, + const statusConfig: Record = { + uploading: { tone: "info", label: "Uploading" }, + done: { tone: "success", label: "Ready" }, + error: { tone: "error", label: "Error" }, + removed: { tone: "neutral", label: "Removed" }, }; - const config = statusConfig[status]; - return ; + const config: { tone: StatusTone; label: string } = statusConfig[status] ?? { tone: "neutral", label: status }; + return ; }; const formatFileSize = (bytes?: number) => { diff --git a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.test.tsx b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.test.tsx index ac790122507..54b05bc73b5 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.test.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.test.tsx @@ -154,9 +154,8 @@ describe("VectorStoreTable", () => { it("should truncate long vector store IDs", () => { renderComponent(); - // Check that the truncated text is rendered (first 15 chars + ...) - const truncatedText = "very-long-vecto..."; - expect(screen.getByText(truncatedText)).toBeInTheDocument(); + const idButton = screen.getByText("very-long-vector-store-id-that-should-be-truncated"); + expect(idButton).toHaveClass("truncate", "max-w-[15ch]"); }); it("should make vector store ID clickable", async () => { @@ -245,13 +244,13 @@ describe("VectorStoreTable", () => { describe("Date Columns", () => { it("should render created at dates", () => { renderComponent(); - const dateElements = screen.getAllByText(/1\/\d+\/2024/); + const dateElements = screen.getAllByText(/Jan \d+, 2024/); expect(dateElements.length).toBe(6); // 3 created_at + 3 updated_at dates }); it("should render updated at dates", () => { renderComponent(); - const dateElements = screen.getAllByText(/1\/\d+\/2024/); + const dateElements = screen.getAllByText(/Jan \d+, 2024/); expect(dateElements.length).toBe(6); // 3 created_at + 3 updated_at dates }); }); diff --git a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx index 180c2485ab5..c2e47cebe69 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx @@ -10,6 +10,7 @@ import { import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "@tremor/react"; import { Tooltip } from "antd"; import React from "react"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import TableIconActionButton from "../common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import { getProviderLogoAndName } from "../provider_info_helpers"; import { VectorStore } from "./types"; @@ -28,19 +29,7 @@ const VectorStoreTable: React.FC = ({ data, onView, onEdi { header: "Vector Store ID", accessorKey: "vector_store_id", - cell: ({ row }) => { - const vectorStore = row.original; - return ( - - ); - }, + cell: ({ row }) => , }, { header: "Name", @@ -109,19 +98,13 @@ const VectorStoreTable: React.FC = ({ data, onView, onEdi header: "Created At", accessorKey: "created_at", sortingFn: "datetime", - cell: ({ row }) => { - const vectorStore = row.original; - return {new Date(vectorStore.created_at).toLocaleDateString()}; - }, + cell: ({ row }) => , }, { header: "Updated At", accessorKey: "updated_at", sortingFn: "datetime", - cell: ({ row }) => { - const vectorStore = row.original; - return {new Date(vectorStore.updated_at).toLocaleDateString()}; - }, + cell: ({ row }) => , }, { id: "actions", diff --git a/ui/litellm-dashboard/src/components/view_logs/audit_logs.tsx b/ui/litellm-dashboard/src/components/view_logs/audit_logs.tsx index 28b7afb6298..d811d3b9402 100644 --- a/ui/litellm-dashboard/src/components/view_logs/audit_logs.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/audit_logs.tsx @@ -4,7 +4,7 @@ import { Table, Tag, Input, Select, Button, Pagination, Spin } from "antd"; import { ReloadOutlined, LoadingOutlined } from "@ant-design/icons"; import type { ColumnsType } from "antd/es/table"; import { resolveLogoSrc } from "@/lib/assetPaths"; -import moment from "moment"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { uiAuditLogsCall } from "../networking"; import { AuditLogEntry } from "./columns"; import { AuditLogDrawer } from "./AuditLogDrawer/AuditLogDrawer"; @@ -95,11 +95,7 @@ export default function AuditLogs({ userID, userRole, token, accessToken, isActi dataIndex: "updated_at", key: "updated_at", width: 200, - render: (val: string) => ( - - {moment.utc(val).local().format("MMM D, YYYY HH:mm:ss")} - - ), + render: (val: string) => , }, { title: "Action", @@ -123,7 +119,7 @@ export default function AuditLogs({ userID, userRole, token, accessToken, isActi title: "Object ID", dataIndex: "object_id", key: "object_id", - render: (val: string) => {val}, + render: (val: string) => , }, { title: "Changed By", @@ -137,7 +133,7 @@ export default function AuditLogs({ userID, userRole, token, accessToken, isActi dataIndex: "changed_by_api_key", key: "changed_by_api_key", width: 140, - render: (val: string) => (val ? {val.slice(0, 12)}… : "—"), + render: (val: string) => , }, ]; diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.test.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.test.tsx new file mode 100644 index 00000000000..afdd17813f3 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/columns.test.tsx @@ -0,0 +1,56 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it } from "vitest"; + +import { createColumns, type LogEntry } from "./columns"; +import { DataTable } from "./table"; + +const logEntry = (overrides: Partial): LogEntry => ({ + request_id: "req-1", + api_key: "key-1", + team_id: "team-1", + model: "gpt-4o", + model_id: "model-1", + call_type: "acompletion", + spend: 0, + total_tokens: 10, + prompt_tokens: 5, + completion_tokens: 5, + startTime: "2026-07-07T09:50:13Z", + endTime: "2026-07-07T09:50:14Z", + cache_hit: "false", + messages: [], + response: {}, + ...overrides, +}); + +describe("Cost column", () => { + it("renders '-' for zero spend with no tooltip, so hovering never shows a contradictory $0", async () => { + const user = userEvent.setup(); + render( + r.request_id} + />, + ); + for (const dash of screen.getAllByText("-")) { + await user.hover(dash); + } + expect(screen.queryByText("$0")).not.toBeInTheDocument(); + }); + + it("shows the full-precision raw value in the tooltip for a real spend", async () => { + const user = userEvent.setup(); + render( + r.request_id} + />, + ); + const formatted = screen.getByText("$0.000123"); + await user.hover(formatted); + expect(await screen.findByText("$0.00012345678")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 7452992ed59..1d0f3f33d08 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -1,11 +1,10 @@ +import { DateCell, IdCell, MoneyCell, StatusBadge } from "@/components/shared/table_cells"; import { getSpendString } from "@/utils/dataUtils"; import type { ColumnDef } from "@tanstack/react-table"; -import { Badge, Button } from "@tremor/react"; import { Tooltip } from "antd"; -import React, { useState } from "react"; +import React from "react"; import { getProviderLogoAndName } from "../provider_info_helpers"; import { TableHeaderSortDropdown } from "../common_components/TableHeaderSortDropdown/TableHeaderSortDropdown"; -import { TimeCell } from "./time_cell"; import { AGENT_CALL_TYPES, MCP_CALL_TYPES } from "./constants"; import { AgentBadge, AgentIcon, LlmBadge, McpBadge, SparkleIcon, WrenchIcon } from "./TypeBadges"; @@ -120,7 +119,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] : "Time", accessorKey: "startTime", size: 200, - cell: (info: any) => , + cell: (info: any) => , }, { header: "Type", @@ -174,48 +173,20 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] cell: (info: any) => { const status = info.getValue() || "Success"; const isSuccess = status.toLowerCase() !== "failure"; - - return ( - - {isSuccess ? "Success" : "Failure"} - - ); + return ; }, }, { header: "Session ID", accessorKey: "session_id", size: 120, - cell: (info: any) => { - const value = String(info.getValue() || ""); - const onSessionClick = info.row.original.onSessionClick; - return ( - - - - ); - }, + cell: (info: any) => , }, { header: "Request ID", accessorKey: "request_id", - cell: (info: any) => ( - - {String(info.getValue() || "")} - - ), + cell: (info: any) => , }, { header: sortProps @@ -236,11 +207,14 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] const row = info.row.original; const mcpCount = row.mcp_tool_call_count || 0; const mcpSpend = row.mcp_tool_call_spend || 0; + const spend = info.getValue(); return (
- - {getSpendString(info.getValue() || 0)} + + + + {mcpCount > 0 && mcpSpend > 0 && ( @@ -320,21 +294,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] header: "Key Hash", accessorKey: "metadata.user_api_key", size: 110, - cell: (info: any) => { - const value = String(info.getValue() || "-"); - const onKeyHashClick = info.row.original.onKeyHashClick; - - return ( - - onKeyHashClick?.(value)} - > - {value} - - - ); - }, + cell: (info: any) => , }, { header: "Key Alias", @@ -589,141 +549,3 @@ export type AuditLogEntry = { before_value: Record; updated_values: Record; }; - -const getActionBadge = (action: string) => { - return ( - - {action} - - ); -}; - -export const auditLogColumns: ColumnDef[] = [ - { - id: "expander", - header: () => null, - cell: ({ row }) => { - const ExpanderCell = () => { - const [localExpanded, setLocalExpanded] = React.useState(row.getIsExpanded()); - - const toggleHandler = React.useCallback(() => { - setLocalExpanded((prev) => !prev); - row.getToggleExpandedHandler()(); - }, [row]); - - return row.getCanExpand() ? ( - - ) : ( - - ); - }; - return ; - }, - }, - { - header: "Timestamp", - accessorKey: "updated_at", - cell: (info: any) => , - }, - { - header: "Table Name", - accessorKey: "table_name", - cell: (info: any) => { - const tableName = info.getValue(); - let displayValue = tableName; - switch (tableName) { - case "LiteLLM_VerificationToken": - displayValue = "Keys"; - break; - case "LiteLLM_TeamTable": - displayValue = "Teams"; - break; - case "LiteLLM_OrganizationTable": - displayValue = "Organizations"; - break; - case "LiteLLM_UserTable": - displayValue = "Users"; - break; - case "LiteLLM_ProxyModelTable": - displayValue = "Models"; - break; - default: - displayValue = tableName; - } - return {displayValue}; - }, - }, - { - header: "Action", - accessorKey: "action", - cell: (info: any) => {getActionBadge(info.getValue())}, - }, - { - header: "Changed By", - accessorKey: "changed_by", - cell: (info: any) => { - const changedBy = info.row.original.changed_by; - const apiKey = info.row.original.changed_by_api_key; - return ( -
-
{changedBy}
- {apiKey && ( // Only show API key if it exists - -
- {" "} - {/* Apply max-width and truncate */} - {apiKey} -
-
- )} -
- ); - }, - }, - { - header: "Affected Item ID", - accessorKey: "object_id", - cell: (props) => { - const ObjectIdDisplay = () => { - const objectId = props.getValue(); - const [copied, setCopied] = useState(false); - - if (!objectId) return <>-; - - const handleCopy = async () => { - try { - await navigator.clipboard.writeText(String(objectId)); - setCopied(true); - setTimeout(() => setCopied(false), 1500); - } catch (err) { - console.error("Failed to copy object ID: ", err); - } - }; - - return ( - - - {String(objectId)} - - - ); - }; - return ; - }, - }, -]; diff --git a/ui/litellm-dashboard/src/components/view_logs/time_cell.test.tsx b/ui/litellm-dashboard/src/components/view_logs/time_cell.test.tsx deleted file mode 100644 index 95a8b43b2c1..00000000000 --- a/ui/litellm-dashboard/src/components/view_logs/time_cell.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; -import { TimeCell, getTimeZone } from "./time_cell"; - -describe("TimeCell", () => { - it("should render a formatted time string", () => { - render(); - // The global toLocaleString mock in setupTests returns "YYYY-MM-DD HH:MM:SS" - expect(screen.getByText(/2025/)).toBeInTheDocument(); - }); - - it("should render 'Error converting time' for invalid dates", () => { - // toLocaleString on an Invalid Date returns "Invalid Date", not throwing, - // but the component catches exceptions. Force an error by passing something - // that causes Date constructor to produce NaN. - render(); - // The mock returns "NaN-NaN-NaN NaN:NaN:NaN" for invalid dates - // The component has a try/catch that returns "Error converting time" on exception - const el = screen.getByText(/NaN|Error/); - expect(el).toBeInTheDocument(); - }); - - it("should render with monospace font", () => { - render(); - const span = screen.getByText(/2025/); - expect(span).toHaveStyle({ fontFamily: "monospace" }); - }); -}); - -describe("getTimeZone", () => { - it("should return a non-empty timezone string", () => { - const tz = getTimeZone(); - expect(typeof tz).toBe("string"); - expect(tz.length).toBeGreaterThan(0); - }); -}); diff --git a/ui/litellm-dashboard/src/components/view_logs/time_cell.tsx b/ui/litellm-dashboard/src/components/view_logs/time_cell.tsx deleted file mode 100644 index 8addc702dc6..00000000000 --- a/ui/litellm-dashboard/src/components/view_logs/time_cell.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from "react"; - -interface TimeCellProps { - utcTime: string; -} - -const getLocalTime = (utcTime: string): string => { - try { - const date = new Date(utcTime); - return date - .toLocaleString("en-US", { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: true, - }) - .replace(",", ""); - } catch (e) { - return "Error converting time"; - } -}; - -export const TimeCell: React.FC = ({ utcTime }) => { - return ( - - {getLocalTime(utcTime)} - - ); -}; - -export const getTimeZone = (): string => { - return Intl.DateTimeFormat().resolvedOptions().timeZone; -}; From 4a25cce114927f34287ec1c9444fc24199c1317c Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 14:57:21 -0700 Subject: [PATCH 028/399] fix(mcp): reject duplicate Authorization headers at MCP ingress For the client-forwarded token modes the gateway relays the caller's Authorization to the upstream, so a request carrying more than one Authorization header would make which token is forwarded ambiguous (the ASGI header list collapses to last-wins) and could diverge from what admission inspected. Multiple Authorization headers is malformed for bearer auth anyway (RFC 9110: not a comma-combinable field), so the ingress header converter now fails closed with a 400 instead of silently keeping one. Applies to every MCP request, not just passthrough. --- .../mcp_server/auth/user_api_key_auth_mcp.py | 31 +++++++++++++++- .../auth/test_user_api_key_auth_mcp.py | 35 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index 9f28da19292..e7ffef0e4e3 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -591,10 +591,19 @@ class MCPRequestHandler: ASGI headers are in format: List[List[bytes, bytes]] We need to convert them to the format Headers expects. + + Collapsing the ASGI list into a dict keeps the last value for a duplicated + header name, so a request carrying more than one ``Authorization`` is + rejected first: for the client-forwarded token modes the gateway relays the + caller's ``Authorization`` upstream, so a duplicate would make which token is + forwarded ambiguous (and diverge from what admission inspected). Multiple + ``Authorization`` headers is malformed for bearer auth anyway (RFC 9110: not + a comma-combinable field), so fail closed with a 400. """ + raw_headers = scope.get("headers", []) + MCPRequestHandler._reject_duplicate_authorization(raw_headers) try: # ASGI headers are list of [name: bytes, value: bytes] pairs - raw_headers = scope.get("headers", []) # Convert bytes to strings and create dict for Headers constructor headers_dict = {name.decode("latin-1"): value.decode("latin-1") for name, value in raw_headers} return Headers(headers_dict) @@ -603,6 +612,26 @@ class MCPRequestHandler: # Return empty Headers object with empty dict return Headers({}) + @staticmethod + def _reject_duplicate_authorization(raw_headers: object) -> None: + """Raise 400 when the raw ASGI headers carry more than one ``Authorization`` header.""" + if not isinstance(raw_headers, (list, tuple)): + return + count = 0 + for entry in raw_headers: + if not isinstance(entry, (list, tuple)) or len(entry) < 1: + continue + name = entry[0] + if isinstance(name, (bytes, bytearray)) and bytes(name).lower() == b"authorization": + count += 1 + elif isinstance(name, str) and name.lower() == "authorization": + count += 1 + if count > 1: + raise HTTPException( + status_code=400, + detail="Multiple Authorization headers are not allowed", + ) + @staticmethod async def get_allowed_mcp_servers( user_api_key_auth: Optional[UserAPIKeyAuth] = None, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index c984ccb783e..ebaa6bc7cc0 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -4,6 +4,7 @@ import sys from unittest.mock import AsyncMock, MagicMock, patch import pytest +from fastapi import HTTPException from fastapi.testclient import TestClient sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path @@ -750,6 +751,40 @@ class TestMCPRequestHandler: # For these tests, mcp_server_auth_headers should be empty assert mcp_server_auth_headers == {} + def test_duplicate_authorization_header_is_rejected(self): + """A request carrying more than one Authorization header is malformed for bearer auth and, + for the client-forwarded token modes, would make which upstream token is forwarded ambiguous. + The ingress header converter must reject it with a 400 rather than silently keeping one.""" + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/tp_server", + "headers": [ + (b"authorization", b"Bearer upstream-token-a"), + (b"authorization", b"Bearer upstream-token-b"), + (b"content-type", b"application/json"), + ], + } + with pytest.raises(HTTPException) as exc_info: + MCPRequestHandler._safe_get_headers_from_scope(scope) + assert exc_info.value.status_code == 400 + assert "Authorization" in str(exc_info.value.detail) + + def test_single_authorization_header_is_forwarded_verbatim(self): + """The rejection must not disturb the normal single-Authorization case: the value passes + through unchanged (guards against the duplicate check over-matching).""" + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/tp_server", + "headers": [ + (b"authorization", b"Bearer upstream-token"), + (b"content-type", b"application/json"), + ], + } + headers = MCPRequestHandler._safe_get_headers_from_scope(scope) + assert headers.get("authorization") == "Bearer upstream-token" + @pytest.mark.asyncio class TestMCPOAuth2AuthFlow: From edf00bbe23c1c5be9363dc94f55ab837ca723005 Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 15:44:36 -0700 Subject: [PATCH 029/399] fix(mcp): recognize per-server auth header at the connect-time preemptive 401 The preemptive 401 for true_passthrough and oauth_delegate only inspected the request-wide Authorization, so a caller who bound the upstream token via the per-server x-mcp-{alias}-authorization header (the required shape in a multi-server aggregate, where the request-wide Authorization is withheld) was spuriously 401'd at initialize even though egress already honors that header. The gate now recognizes the per-server header for both modes via a shared helper, mode-correctly: true_passthrough treats any Authorization or the per-server header as the upstream token, oauth_delegate keeps requiring a distinct x-litellm-api-key so a lone Authorization consumed for admission is never mistaken for an upstream token. The preemptive raise is also gated to single-server scopes so a multi-server aggregate degrades gracefully instead of one missing token 401-ing the whole connect. --- .../proxy/_experimental/mcp_server/server.py | 64 +++++++++---- .../mcp_server/test_mcp_stale_session.py | 95 +++++++++++++++++++ 2 files changed, 139 insertions(+), 20 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 4fda15f664e..d4597bf1adf 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1441,6 +1441,35 @@ if MCP_AVAILABLE: return allowed_mcp_servers + def _client_has_per_server_auth_header( + server: MCPServer, + mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]], + ) -> bool: + """True if the request carries a per-server ``x-mcp-{alias}-authorization`` + header for this server. This is the multi-server binding: it names one + upstream, so it is unambiguously the caller's upstream token regardless of + auth mode (never the LiteLLM admission credential). + """ + if not mcp_server_auth_headers: + return False + for key in (server.alias, server.server_name, server.name): + if not key: + continue + server_headers = None + for k, v in mcp_server_auth_headers.items(): + if k.lower() == key.lower(): + server_headers = v + break + if server_headers is None: + continue + if isinstance(server_headers, str) and server_headers.strip(): + return True + if isinstance(server_headers, dict): + for hk in server_headers.keys(): + if hk.lower() == "authorization": + return True + return False + def _client_has_passthrough_authorization( server: MCPServer, oauth2_headers: Optional[Dict[str, str]], @@ -1458,24 +1487,7 @@ if MCP_AVAILABLE: for k in oauth2_headers.keys(): if k.lower() == "authorization": return True - if mcp_server_auth_headers: - for key in (server.alias, server.server_name, server.name): - if not key: - continue - server_headers = None - for k, v in mcp_server_auth_headers.items(): - if k.lower() == key.lower(): - server_headers = v - break - if server_headers is None: - continue - if isinstance(server_headers, str) and server_headers.strip(): - return True - if isinstance(server_headers, dict): - for hk in server_headers.keys(): - if hk.lower() == "authorization": - return True - return False + return _client_has_per_server_auth_header(server, mcp_server_auth_headers) async def _get_user_oauth_extra_headers_from_db( server: MCPServer, @@ -3540,7 +3552,13 @@ if MCP_AVAILABLE: headers={"www-authenticate": www_authenticate}, ) - if server and server.is_oauth_delegate and _get_forwarded_auth_from_scope(scope) is None: + if ( + server + and server.is_oauth_delegate + and len(mcp_servers or []) == 1 + and _get_forwarded_auth_from_scope(scope) is None + and not _client_has_per_server_auth_header(server, mcp_server_auth_headers) + ): www_authenticate = _get_passthrough_www_authenticate( scope=scope, server_name=server_name, @@ -3551,7 +3569,13 @@ if MCP_AVAILABLE: headers={"www-authenticate": www_authenticate}, ) - if server and server.is_true_passthrough and not _scope_has_authorization_header(scope): + if ( + server + and server.is_true_passthrough + and len(mcp_servers or []) == 1 + and not _scope_has_authorization_header(scope) + and not _client_has_per_server_auth_header(server, mcp_server_auth_headers) + ): upstream_status, upstream_www_authenticate = await _probe_upstream_auth(server.url or "", "") if upstream_status == 401 and upstream_www_authenticate: raise HTTPException( diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py index 44ea5f43a70..04856b33f2a 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py @@ -1216,6 +1216,101 @@ async def test_handle_streamable_http_mcp_oauth_delegate_with_forwarded_token_sk assert mock_handle_request.await_count == 1 +async def _run_passthrough_connect( + *, + auth_type, + server_names, + mcp_server_auth_headers, + scope_extra_headers=None, +): + """Drive handle_streamable_http_mcp through the preemptive-401 gate and report whether it + challenged (raised) or forwarded to the session manager. Returns (challenged, www_authenticate).""" + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateless, + ) + + scope = _passthrough_mode_scope(server_names[0], extra_headers=scope_extra_headers) + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = "u1" + server = _build_passthrough_mode_server(server_names[0], auth_type) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, server_names, mcp_server_auth_headers, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch("litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._check_passthrough_upstream_auth", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=server, + ), + patch.object(session_manager_stateless, "handle_request", new_callable=AsyncMock) as mock_handle_request, + patch.object(session_manager_stateless, "_server_instances", {}), + ): + try: + await handle_streamable_http_mcp(scope, receive, send) + except HTTPException as exc: + return True, (exc.headers or {}).get("www-authenticate") + return mock_handle_request.await_count == 0, None + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) +async def test_handle_streamable_http_mcp_per_server_header_skips_preemptive_challenge(auth_type): + """A per-server x-mcp-{alias}-authorization header binds the upstream token to one server; the + connect gate must recognize it and forward instead of spuriously 401-ing, since egress already + honors it. Without this, the mandatory multi-server binding is unusable at connect.""" + try: + from litellm.proxy._experimental.mcp_server.server import handle_streamable_http_mcp # noqa: F401 + except ImportError: + pytest.skip("MCP server not available") + + challenged, _ = await _run_passthrough_connect( + auth_type=auth_type, + server_names=["pt_server"], + mcp_server_auth_headers={"pt_server": {"Authorization": "Bearer upstream-token"}}, + ) + assert challenged is False + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) +async def test_handle_streamable_http_mcp_aggregate_does_not_preemptively_challenge(auth_type): + """A multi-server aggregate must degrade gracefully: the preemptive 401 is single-server only, so + one server missing a token cannot 401 the whole connect (the listing absorbs per-server failures).""" + try: + from litellm.proxy._experimental.mcp_server.server import handle_streamable_http_mcp # noqa: F401 + except ImportError: + pytest.skip("MCP server not available") + + challenged, _ = await _run_passthrough_connect( + auth_type=auth_type, + server_names=["pt_server", "pt_server_2"], + mcp_server_auth_headers=None, + ) + assert challenged is False + + @pytest.mark.asyncio async def test_handle_streamable_http_mcp_true_passthrough_without_token_surfaces_verbatim_upstream_challenge(): """true_passthrough is a transparent proxy: with no client Authorization the From ddec3b2b8b8846656611110c493307c3f5f70e53 Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 15:46:39 -0700 Subject: [PATCH 030/399] fix(mcp): plug fan-out Authorization bypass in the extra_headers loop The listing fan-out withholds the request-wide Authorization from a true_passthrough / oauth_delegate server when another server in scope also consumes it, so one bearer is not replayed across upstreams. The later server.extra_headers copy loop did not honor that decision: a server listing Authorization in extra_headers would re-copy the withheld bearer from raw_headers. The withhold decision is now computed once and applied to both the forwarding branch and the extra_headers loop. --- .../proxy/_experimental/mcp_server/server.py | 18 ++++++-- .../mcp_server/test_mcp_server.py | 43 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index d4597bf1adf..25890d29368 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1565,6 +1565,16 @@ if MCP_AVAILABLE: ) extra_headers: Optional[Dict[str, str]] = None + is_client_forwarded_mode = server.is_true_passthrough or server.is_oauth_delegate + # In a multi-server listing scope the request-wide Authorization can only carry one token, + # so it is withheld from a client-forwarded server when another server in scope also consumes + # it (RFC 9700 cross-resource replay); such scopes must bind per-server via + # x-mcp-{alias}-authorization. The decision is computed once so BOTH the forwarding branch and + # the extra_headers copy loop below honor it — otherwise a server that lists Authorization in + # extra_headers would re-copy the withheld bearer from raw_headers and replay it anyway. + withhold_forwarded_authorization = is_client_forwarded_mode and _caller_authorization_fans_out( + server, scope_servers + ) if server.auth_type == MCPAuth.oauth2: # For OAuth2 M2M servers, upstream Authorization must come from # client_credentials token fetch, never from caller headers. @@ -1583,8 +1593,8 @@ if MCP_AVAILABLE: user_api_key_auth=user_api_key_auth, ): extra_headers = _without_authorization(extra_headers) - elif server.is_true_passthrough or server.is_oauth_delegate: - if not _caller_authorization_fans_out(server, scope_servers): + elif is_client_forwarded_mode: + if not withhold_forwarded_authorization: extra_headers = _client_forwarded_authorization_headers( mcp_server=server, oauth2_headers=oauth2_headers, @@ -1611,7 +1621,9 @@ if MCP_AVAILABLE: for header in server.extra_headers: if not isinstance(header, str): continue - if header.lower() == "authorization" and strip_caller_authorization: + if header.lower() == "authorization" and ( + strip_caller_authorization or withhold_forwarded_authorization + ): continue header_value = normalized_raw_headers.get(header.lower()) if header_value is None: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index 71ece75b2fb..12ebc31e33c 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -426,6 +426,49 @@ def test_prepare_mcp_server_headers_scope_counts_legacy_delegate_as_consumer(): assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} +def test_prepare_mcp_server_headers_fanout_withhold_survives_extra_headers_loop(): + """Regression: when fan-out withholds the request-wide Authorization from a client-forwarded + server, the later server.extra_headers copy loop must not re-add it from raw_headers even if + the server lists Authorization in extra_headers. Otherwise one bearer is replayed across every + consuming upstream in the scope (the exact cross-resource replay the withholding prevents).""" + delegate = MCPServer( + server_id="od-extra-hdr", + name="od-extra-hdr", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + extra_headers=["Authorization"], + ) + second_consumer = _client_forwarded_mode_server("tp-peer", MCPAuth.true_passthrough) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, second_consumer]) + + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + +def test_prepare_mcp_server_headers_sole_consumer_still_forwards_via_extra_headers(): + """Guard the fix does not over-withhold: with no second consumer in scope, a client-forwarded + server that lists Authorization in extra_headers still forwards the caller's bearer.""" + delegate = MCPServer( + server_id="od-extra-sole", + name="od-extra-sole", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + extra_headers=["Authorization"], + ) + static_server = MCPServer( + server_id="static-peer", + name="static-peer", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="static-key", + ) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, static_server]) + + assert extra_headers is not None + assert extra_headers.get("Authorization") == "Bearer upstream-token" + + @pytest.mark.asyncio async def test_call_tool_m2m_skips_authorization_headers(): """M2M call_tool must not forward caller Authorization in oauth2/raw headers.""" From b2ea36f4f11ddc31122027ffb6eea225d5a485fd Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 16:22:05 -0700 Subject: [PATCH 031/399] fix(mcp): match sanitized per-server alias at the connect-time preemptive 401 The connect gate resolved x-mcp-{alias}-authorization by matching the raw lowercased alias/server_name/name only, but dashboard clients send x-mcp-{sanitize_mcp_alias_for_header(alias)}-authorization, and egress resolves those through lookup_mcp_server_auth_in_headers, which also tries the sanitized alias. So a per-server token bound with a sanitized alias (e.g. alias 'pt-server' arriving as header key 'pt_server') was forwarded at egress but still triggered a preemptive 401 at connect. _client_has_per_server_auth_header now resolves through the same lookup_mcp_server_auth_in_headers egress uses, so connect and egress agree on which header names match. --- .../proxy/_experimental/mcp_server/server.py | 32 +++++++++---------- .../mcp_server/test_mcp_stale_session.py | 19 +++++++++++ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 25890d29368..7e19c44052d 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1449,25 +1449,25 @@ if MCP_AVAILABLE: header for this server. This is the multi-server binding: it names one upstream, so it is unambiguously the caller's upstream token regardless of auth mode (never the LiteLLM admission credential). + + Resolves through the same ``lookup_mcp_server_auth_in_headers`` egress uses, so + the connect gate and egress agree on which per-server header names match: a + dashboard client sends ``x-mcp-{sanitize_mcp_alias_for_header(alias)}-authorization``, + and matching only the raw alias here would 401 a token egress would forward. """ if not mcp_server_auth_headers: return False - for key in (server.alias, server.server_name, server.name): - if not key: - continue - server_headers = None - for k, v in mcp_server_auth_headers.items(): - if k.lower() == key.lower(): - server_headers = v - break - if server_headers is None: - continue - if isinstance(server_headers, str) and server_headers.strip(): - return True - if isinstance(server_headers, dict): - for hk in server_headers.keys(): - if hk.lower() == "authorization": - return True + from litellm.proxy._experimental.mcp_server.utils import ( + lookup_mcp_server_auth_in_headers, + ) + + server_headers = lookup_mcp_server_auth_in_headers( + mcp_server_auth_headers, alias=server.alias, server_name=server.server_name + ) + if isinstance(server_headers, str): + return bool(server_headers.strip()) + if isinstance(server_headers, dict): + return any(isinstance(hk, str) and hk.lower() == "authorization" for hk in server_headers) return False def _client_has_passthrough_authorization( diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py index 04856b33f2a..da183e8d02a 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py @@ -1293,6 +1293,25 @@ async def test_handle_streamable_http_mcp_per_server_header_skips_preemptive_cha assert challenged is False +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) +async def test_handle_streamable_http_mcp_sanitized_per_server_header_skips_preemptive_challenge(auth_type): + """A dashboard client sends x-mcp-{sanitize_mcp_alias_for_header(alias)}-authorization, so the + alias 'pt-server' arrives as the header key 'pt_server'. Egress resolves that via the sanitized + alias, so the connect gate must too, or it 401s a token egress would forward.""" + try: + from litellm.proxy._experimental.mcp_server.server import handle_streamable_http_mcp # noqa: F401 + except ImportError: + pytest.skip("MCP server not available") + + challenged, _ = await _run_passthrough_connect( + auth_type=auth_type, + server_names=["pt-server"], + mcp_server_auth_headers={"pt_server": {"Authorization": "Bearer upstream-token"}}, + ) + assert challenged is False + + @pytest.mark.asyncio @pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) async def test_handle_streamable_http_mcp_aggregate_does_not_preemptively_challenge(auth_type): From 5aa5b8ef32776770e8f51dbe7485a5ebba76ac91 Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:30:46 -0700 Subject: [PATCH 032/399] fix(vertex): forward realtime health check params (#32550) * fix(vertex): forward realtime health check params * refactor(vertex): resolve realtime health check params via VertexBase helpers Address review feedback on the vertex param forwarding: pass model_params through to _realtime_health_check and resolve vertex credentials, project, and location inside the vertex_ai branch using the existing VertexBase.safe_get_vertex_ai_* helpers, so provider-specific key extraction no longer lives in litellm_core_utils and dict-typed vertex_credentials are supported * test(vertex): move realtime health check test to mapped unit test path codecov/patch reported the vertex branch of _realtime_health_check as uncovered because tests/litellm_utils_tests is not part of the unit test groups that upload coverage. Move the test into tests/test_litellm/litellm_core_utils/test_health_check_helpers.py, which the core-utils group runs, keeping the same end-to-end assertions through litellm.ahealth_check --------- Co-authored-by: Aleksandr Liadov <72351793+AleksandrLiadov@users.noreply.github.com> --- .../health_check_helpers.py | 1 + litellm/realtime_api/main.py | 12 ++-- .../test_health_check_helpers.py | 67 +++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/litellm/litellm_core_utils/health_check_helpers.py b/litellm/litellm_core_utils/health_check_helpers.py index 42ac82abf8b..9fc036e2a99 100644 --- a/litellm/litellm_core_utils/health_check_helpers.py +++ b/litellm/litellm_core_utils/health_check_helpers.py @@ -199,6 +199,7 @@ class HealthCheckHelpers: api_base=model_params.get("api_base", None), api_key=model_params.get("api_key", None), api_version=model_params.get("api_version", None), + model_params=model_params, ), "batch": lambda: HealthCheckHelpers._batch_health_check( custom_llm_provider=custom_llm_provider, diff --git a/litellm/realtime_api/main.py b/litellm/realtime_api/main.py index 0566ff73683..5ecf4d91ff6 100644 --- a/litellm/realtime_api/main.py +++ b/litellm/realtime_api/main.py @@ -515,6 +515,7 @@ async def _realtime_health_check( api_base: Optional[str] = None, api_version: Optional[str] = None, realtime_protocol: Optional[str] = None, + model_params: Optional[dict] = None, ): """ Health check for realtime API - tries connection to the realtime API websocket @@ -550,14 +551,17 @@ async def _realtime_health_check( elif custom_llm_provider == "xai": url = xai_realtime._construct_url(api_base=api_base or "https://api.x.ai/v1", query_params={"model": model}) elif custom_llm_provider == "vertex_ai": - vertex_location = litellm.vertex_location or get_secret_str("VERTEXAI_LOCATION") - resolved_location = vertex_llm_base.get_vertex_region(vertex_region=vertex_location, model=model) + vertex_model_params = model_params or {} + resolved_location = vertex_llm_base.get_vertex_region( + vertex_region=VertexBase.safe_get_vertex_ai_location(vertex_model_params), + model=model, + ) ( access_token, resolved_project, ) = await vertex_llm_base._ensure_access_token_async( - credentials=None, - project_id=litellm.vertex_project or get_secret_str("VERTEXAI_PROJECT"), + credentials=VertexBase.safe_get_vertex_ai_credentials(vertex_model_params), + project_id=VertexBase.safe_get_vertex_ai_project(vertex_model_params), custom_llm_provider="vertex_ai", ) vertex_realtime_config = VertexAIRealtimeConfig( diff --git a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py index e8ef8f15142..f0d91224614 100644 --- a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py @@ -277,3 +277,70 @@ async def test_batch_health_check_falls_back_to_acompletion_for_unsupported(): ) mock_alist.assert_not_called() mock_acompletion.assert_called_once_with(**model_params) + + +class _FakeWebsocketConnect: + def __init__(self, calls, url, **kwargs): + calls.append({"url": url, **kwargs}) + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + return False + + +@pytest.mark.asyncio +async def test_realtime_health_check_uses_model_level_vertex_params(): + """Regression test: realtime health checks must resolve vertex_credentials, + vertex_project, and vertex_location from the model row's params instead of + falling back to process-global VERTEXAI_* settings.""" + import litellm + from litellm.realtime_api import main as realtime_main + + fake_vertex_base = MagicMock() + fake_vertex_base.get_vertex_region = MagicMock(return_value="us-central1") + fake_vertex_base._ensure_access_token_async = AsyncMock( + return_value=("model-level-token", "model-level-project") + ) + connect_calls = [] + + with ( + patch.object(realtime_main, "vertex_llm_base", fake_vertex_base), + patch( + "websockets.connect", + lambda url, **kwargs: _FakeWebsocketConnect(connect_calls, url, **kwargs), + ), + patch.object( + HealthCheckHelpers, + "_update_model_params_with_health_check_tracking_information", + staticmethod(lambda model_params: model_params), + ), + ): + result = await litellm.ahealth_check( + model_params={ + "model": "vertex_ai/gemini-live-2.5-flash-native-audio", + "vertex_credentials": '{"type":"service_account"}', + "vertex_project": "model-level-project", + "vertex_location": "us-central1", + }, + mode="realtime", + ) + + assert result == {} + fake_vertex_base.get_vertex_region.assert_called_once_with( + vertex_region="us-central1", model="gemini-live-2.5-flash-native-audio" + ) + fake_vertex_base._ensure_access_token_async.assert_called_once_with( + credentials='{"type":"service_account"}', + project_id="model-level-project", + custom_llm_provider="vertex_ai", + ) + assert connect_calls[0]["url"] == ( + "wss://us-central1-aiplatform.googleapis.com/ws/" + "google.cloud.aiplatform.v1.LlmBidiService/BidiGenerateContent" + ) + assert connect_calls[0]["additional_headers"] == { + "Authorization": "Bearer model-level-token", + "x-goog-user-project": "model-level-project", + } From e1b9ec1cd62ce436b863db1fa07179693592b364 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:37:38 -0700 Subject: [PATCH 033/399] feat(pricing): add xai/grok-4.5 model pricing and metadata (#32549) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 42 +++++++++++++++++++ model_prices_and_context_window.json | 42 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 70b6b05e6ec..db534b52df9 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -38101,6 +38101,48 @@ "supports_vision": true, "supports_web_search": true }, + "xai/grok-4.5": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "xai/grok-4.5-latest": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "xai/grok-beta": { "input_cost_per_token": 5e-06, "litellm_provider": "xai", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index b961c326625..363ba9842b0 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -38303,6 +38303,48 @@ "supports_vision": true, "supports_web_search": true }, + "xai/grok-4.5": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "xai/grok-4.5-latest": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "xai/grok-beta": { "input_cost_per_token": 5e-06, "litellm_provider": "xai", From 9d745486d0b96d55e4a2809ea2cdea9465ddca2a Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:48:03 -0700 Subject: [PATCH 034/399] fix(rerank): log optional_rerank_params at debug to stop leaking request content (#32533) * fix(rerank): log optional_rerank_params at debug not info to avoid leaking request content * test(rerank): exercise sync rerank path so coverage counts the log line --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/rerank_api/main.py | 2 +- tests/test_litellm/rerank_api/__init__.py | 0 tests/test_litellm/rerank_api/test_main.py | 67 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/test_litellm/rerank_api/__init__.py create mode 100644 tests/test_litellm/rerank_api/test_main.py diff --git a/litellm/rerank_api/main.py b/litellm/rerank_api/main.py index 9320c7fae8a..b6ebf5589b7 100644 --- a/litellm/rerank_api/main.py +++ b/litellm/rerank_api/main.py @@ -158,7 +158,7 @@ def rerank( instruction=instruction, non_default_params=kwargs, ) - verbose_logger.info(f"optional_rerank_params: {optional_rerank_params}") + verbose_logger.debug(f"optional_rerank_params: {optional_rerank_params}") if isinstance(optional_params.timeout, str): optional_params.timeout = float(optional_params.timeout) diff --git a/tests/test_litellm/rerank_api/__init__.py b/tests/test_litellm/rerank_api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/rerank_api/test_main.py b/tests/test_litellm/rerank_api/test_main.py new file mode 100644 index 00000000000..46d1461da50 --- /dev/null +++ b/tests/test_litellm/rerank_api/test_main.py @@ -0,0 +1,67 @@ +import logging +import os +import sys +from unittest.mock import MagicMock, patch + +sys.path.insert(0, os.path.abspath("../../..")) + +import litellm + +MARKER_QUERY = "MARKER_QUERY_do_not_log_at_info" +MARKER_DOC = "MARKER_DOC_sensitive_customer_text" + + +def _mock_cohere_response() -> MagicMock: + mock_response = MagicMock() + + def return_val(): + return { + "id": "cmpl-mockid", + "results": [{"index": 0, "relevance_score": 0.95}], + "meta": { + "api_version": {"version": "1.0"}, + "billed_units": {"search_units": 1}, + }, + } + + mock_response.json = return_val + mock_response.headers = {"key": "value"} + mock_response.status_code = 200 + return mock_response + + +def test_rerank_does_not_log_request_content_at_info(caplog): + """Regression for #32525: rerank must not emit query/documents to logs at INFO. + + The mapped ``optional_rerank_params`` (which always contains ``query`` and + ``documents``) bypasses ``turn_off_message_logging`` / ``redact_messages``, + so logging it at INFO leaks raw request content into stdout and any log sink. + """ + litellm.cohere_key = "test_api_key" + caplog.set_level(logging.DEBUG, logger="LiteLLM") + + with patch( + "litellm.llms.custom_httpx.http_handler.HTTPHandler.post", + return_value=_mock_cohere_response(), + ): + litellm.rerank( + model="cohere/rerank-english-v3.0", + query=MARKER_QUERY, + documents=[MARKER_DOC, "unrelated"], + top_n=2, + ) + + litellm_records = [r for r in caplog.records if r.name == "LiteLLM"] + + info_or_above = [ + r.getMessage() + for r in litellm_records + if r.levelno >= logging.INFO and (MARKER_QUERY in r.getMessage() or MARKER_DOC in r.getMessage()) + ] + assert not info_or_above, f"rerank leaked request content at INFO+: {info_or_above}" + + optional_params_logs = [r for r in litellm_records if "optional_rerank_params" in r.getMessage()] + assert optional_params_logs, "expected the optional_rerank_params line to be logged" + assert all( + r.levelno == logging.DEBUG for r in optional_params_logs + ), "optional_rerank_params must be logged at DEBUG, not INFO" From 637352735f2053e9326cf9c5c379548e2d00d7ce Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 18:35:05 -0700 Subject: [PATCH 035/399] fix(proxy): resolve team org from team_id so org admins can update team budgets An org admin updating a team budget from the Hub UI was rejected with 401, because the route gate only recognizes an org admin when the request body carries organization_id while the UI sends team_id. For /team/update, resolve the target team's organization_id from team_id before the gate runs, so an org admin of the team's own org clears the org-scoped branch without the client passing organization_id. Team admins and cross-org admins stay denied at the gate, and callers that already pass organization_id are unaffected, so the existing /team/update authorization matrix is unchanged --- litellm/proxy/auth/auth_checks.py | 25 +++- .../proxy/auth/auth_checks_organization.py | 32 ++++- .../management/test_team_update.py | 24 ++-- .../proxy/auth/test_route_checks.py | 129 ++++++++++++++++++ 4 files changed, 199 insertions(+), 11 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index e7fee8d6eb2..cd8103abf5e 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -98,7 +98,10 @@ from litellm.repositories.user_repository import UserRepository from litellm.router import Router from litellm.utils import get_utc_datetime -from .auth_checks_organization import organization_role_based_access_check +from .auth_checks_organization import ( + add_team_org_context_to_request_body, + organization_role_based_access_check, +) from .auth_utils import get_model_from_request if TYPE_CHECKING: @@ -707,10 +710,28 @@ async def common_checks( # 10 [OPTIONAL] Organization RBAC checks organization_role_based_access_check(user_object=user_object, route=route, request_body=request_body) + async def _fetch_team_org_id(team_id: str) -> Optional[str]: + try: + team = await get_team_object( + team_id=team_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + except HTTPException: + return None + return team.organization_id + + request_body_for_route_check = await add_team_org_context_to_request_body( + route=route, + request_body=request_body, + fetch_team_org_id=_fetch_team_org_id, + ) + _is_route_allowed = _is_api_route_allowed( route=route, request=request, - request_data=request_body, + request_data=request_body_for_route_check, valid_token=valid_token, user_obj=user_object, ) diff --git a/litellm/proxy/auth/auth_checks_organization.py b/litellm/proxy/auth/auth_checks_organization.py index 44c1d158cbe..b4caff9b8ee 100644 --- a/litellm/proxy/auth/auth_checks_organization.py +++ b/litellm/proxy/auth/auth_checks_organization.py @@ -2,7 +2,7 @@ Auth Checks for Organizations """ -from typing import Dict, List, Optional, Tuple +from typing import Awaitable, Callable, Dict, List, Optional, Tuple from fastapi import status @@ -170,3 +170,33 @@ def _user_is_org_admin( # User must be admin of ALL requested orgs, not just any one return all(org_id in admin_org_ids for org_id in candidate_org_ids) + + +TEAM_ORG_CONTEXT_ROUTES = frozenset({"/team/update"}) + + +async def add_team_org_context_to_request_body( + route: str, + request_body: dict, + fetch_team_org_id: Callable[[str], Awaitable[Optional[str]]], +) -> dict: + """ + Return a copy of request_body with organization_id resolved from the target + team when the route identifies the team by team_id and the caller did not + pass organization_id. This lets an org admin of the team's own org reach the + org-scoped branch of the route gate (which keys off organization_id) without + the client having to send it. Returns request_body unchanged when it does + not apply, so callers that already pass organization_id and non-team routes + are untouched. + """ + if route not in TEAM_ORG_CONTEXT_ROUTES: + return request_body + if request_body.get("organization_id"): + return request_body + team_id = request_body.get("team_id") + if not isinstance(team_id, str) or not team_id: + return request_body + org_id = await fetch_team_org_id(team_id) + if not org_id: + return request_body + return {**request_body, "organization_id": org_id} diff --git a/tests/proxy_behavior/management/test_team_update.py b/tests/proxy_behavior/management/test_team_update.py index 9cb2b0fecda..23ea89fa74d 100644 --- a/tests/proxy_behavior/management/test_team_update.py +++ b/tests/proxy_behavior/management/test_team_update.py @@ -105,27 +105,35 @@ async def test_team_update_authz_matrix( assert row.team_alias != MARKER_ALIAS, "denied but team mutated" -async def test_team_update_requires_proxy_admin_without_org_context( +async def test_team_update_org_admin_resolved_from_team_without_org_context( proxy_client, prisma, scratch, world ): - """With no organization_id in the body the route gate has no org context - and falls back to proxy-admin-only: an org admin of the team's own org - is 401, PROXY_ADMIN is 200.""" + """With no organization_id in the body the route gate resolves the target + team's org from team_id, so an org admin of the team's own org is allowed + (200), same as PROXY_ADMIN. A team admin of that same team stays denied + (401): the resolution grants org admins access, not team admins.""" await _seed_target(prisma, world, "alpha", scratch.prefix) - denied = await proxy_client.post( + allowed_org_admin = await proxy_client.post( "/team/update", headers={"Authorization": f"Bearer {world.keys[Actor.ORG_ADMIN].cleartext}"}, json={"team_id": scratch.prefix, "team_alias": MARKER_ALIAS}, ) - assert denied.status_code == 401, denied.text + assert allowed_org_admin.status_code == 200, allowed_org_admin.text - allowed = await proxy_client.post( + allowed_proxy_admin = await proxy_client.post( "/team/update", headers={"Authorization": f"Bearer {world.keys[Actor.PROXY_ADMIN].cleartext}"}, json={"team_id": scratch.prefix, "team_alias": MARKER_ALIAS}, ) - assert allowed.status_code == 200, allowed.text + assert allowed_proxy_admin.status_code == 200, allowed_proxy_admin.text + + denied_team_admin = await proxy_client.post( + "/team/update", + headers={"Authorization": f"Bearer {world.keys[Actor.TEAM_ADMIN].cleartext}"}, + json={"team_id": scratch.prefix, "team_alias": MARKER_ALIAS}, + ) + assert denied_team_admin.status_code == 401, denied_team_admin.text # Relocation gate — moving a team to a different org. The scratch team starts diff --git a/tests/test_litellm/proxy/auth/test_route_checks.py b/tests/test_litellm/proxy/auth/test_route_checks.py index d623149ff6a..204e6a671e3 100644 --- a/tests/test_litellm/proxy/auth/test_route_checks.py +++ b/tests/test_litellm/proxy/auth/test_route_checks.py @@ -2595,6 +2595,135 @@ def test_org_admin_of_multiple_orgs_can_operate_on_both(): assert _user_is_org_admin({"organizations": ["org-A", "org-B"]}, user_obj) is True +# ── LIT-4221: /team/update org-context resolution from team_id ──────────────── +from litellm.proxy.auth.auth_checks_organization import ( + add_team_org_context_to_request_body, +) + + +@pytest.mark.asyncio +async def test_add_team_org_context_resolves_org_from_team(): + """For /team/update with only team_id, the target team's org is resolved and + injected so the org-admin route gate can see it. This is what lets an org + admin update a team budget from the Hub UI, which sends team_id, not + organization_id (LIT-4221).""" + + async def fetch(team_id: str): + assert team_id == "team-1" + return "org-1" + + out = await add_team_org_context_to_request_body( + route="/team/update", + request_body={"team_id": "team-1", "max_budget": 42}, + fetch_team_org_id=fetch, + ) + assert out == {"team_id": "team-1", "max_budget": 42, "organization_id": "org-1"} + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_when_org_id_already_present(): + """If the caller already passed organization_id, no lookup happens and the + body is returned unchanged.""" + + async def fetch(team_id: str): + raise AssertionError("must not resolve when organization_id is present") + + body = {"team_id": "team-1", "organization_id": "org-explicit"} + out = await add_team_org_context_to_request_body( + route="/team/update", request_body=body, fetch_team_org_id=fetch + ) + assert out == body + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_for_other_routes(): + """Only /team/update opts into org resolution; other routes are untouched.""" + + async def fetch(team_id: str): + raise AssertionError("must not resolve for a non-opted-in route") + + body = {"team_id": "team-1"} + out = await add_team_org_context_to_request_body( + route="/team/delete", request_body=body, fetch_team_org_id=fetch + ) + assert out == body + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_when_team_has_no_org(): + """A standalone team (no org) resolves to None, so nothing is injected and + the org-admin branch stays unreachable (no blanket access).""" + + async def fetch(team_id: str): + return None + + body = {"team_id": "team-1"} + out = await add_team_org_context_to_request_body( + route="/team/update", request_body=body, fetch_team_org_id=fetch + ) + assert out == body + + +def test_team_update_gate_allows_org_admin_with_resolved_org(): + """Post-resolution (organization_id present), an org admin of that org clears + the gate for /team/update.""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + request = MagicMock(spec=Request) + request.method = "POST" + request.query_params = {} + + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/update", + request=request, + valid_token=valid_token, + request_data={"team_id": "team-1", "organization_id": "org-1"}, + ) + + +def test_team_update_gate_rejects_without_org_context(): + """Without organization_id (i.e. resolution found no org, or a non-org-admin), + the gate still rejects /team/update — the fix adds no blanket allow. Guards + against re-widening the route (e.g. dropping it into self_managed_routes).""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + request = MagicMock(spec=Request) + request.method = "POST" + request.query_params = {} + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/update", + request=request, + valid_token=valid_token, + request_data={"team_id": "team-1", "max_budget": 42}, + ) + + +def test_team_update_gate_rejects_cross_org_admin_with_resolved_org(): + """Even after the target team's org is resolved, an org admin of a DIFFERENT + org is rejected at the gate (no cross-org escalation).""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + request = MagicMock(spec=Request) + request.method = "POST" + request.query_params = {} + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/update", + request=request, + valid_token=valid_token, + request_data={"team_id": "team-1", "organization_id": "org-2"}, + ) + + @pytest.mark.asyncio async def test_initialize_pass_through_registers_wildcard_for_auth_subpath(): """ From febb27695b72e1aea5e9cfa4d3173291863361dc Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Wed, 8 Jul 2026 19:47:05 -0700 Subject: [PATCH 036/399] refactor(ui): point invitation links at the dedicated /onboarding route (#30857) * refactor(ui): point invitation links at the dedicated /onboarding route Invitation and reset-password links were built as /ui?invitation_id=..., which lands on the dashboard index and renders the onboarding form inline. They now point at the standalone /ui/onboarding route, so the index no longer has to special-case invitations. Old links keep working unchanged; the index still renders onboarding inline for ?invitation_id until the migration closeout removes that branch. Updates the three generators (the enterprise email builder, bulk user create, and the invitation/reset-password modal) and extracts the modal's URL building into a pure, unit-tested buildOnboardingUrl Refs LIT-3687 * refactor(ui): guard buildOnboardingUrl against a missing invitation id Return "" instead of emitting an invitation_id=undefined link when the id is not yet available, matching the existing empty-baseUrl guard. Placed after the SSO branch so the SSO link, which does not use the id, is unaffected Refs LIT-3687 --- .../send_emails/base_email.py | 4 +- .../send_emails/test_base_email.py | 27 +++++-- .../components/bulk_create_users_button.tsx | 2 +- .../src/components/onboarding_link.test.tsx | 70 +++++++++++++++++++ .../src/components/onboarding_link.tsx | 51 +++++++++----- 5 files changed, 126 insertions(+), 28 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/onboarding_link.test.tsx diff --git a/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py index be80a12c80a..e7898cac565 100644 --- a/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py +++ b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py @@ -919,9 +919,9 @@ class BaseEmailLogger(CustomLogger): """ Construct invitation link for the user - # http://localhost:4000/ui?invitation_id=7a096b3a-37c6-440f-9dd1-ba22e8043f6b + # http://localhost:4000/ui/onboarding?invitation_id=7a096b3a-37c6-440f-9dd1-ba22e8043f6b """ - return f"{base_url}/ui?invitation_id={invitation_id}" + return f"{base_url}/ui/onboarding?invitation_id={invitation_id}" async def send_email( self, diff --git a/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py b/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py index c1ccc454305..61303340570 100644 --- a/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py +++ b/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py @@ -348,7 +348,9 @@ async def test_get_invitation_link(base_email_logger): result = await base_email_logger._get_invitation_link( user_id="test-user", base_url="http://test.com" ) - assert result == "http://test.com/ui?invitation_id=test-invitation-id" + assert ( + result == "http://test.com/ui/onboarding?invitation_id=test-invitation-id" + ) # Test with None user_id result = await base_email_logger._get_invitation_link( @@ -372,7 +374,7 @@ def test_construct_invitation_link(base_email_logger): result = base_email_logger._construct_invitation_link( invitation_id="test-id-123", base_url="http://test.com" ) - assert result == "http://test.com/ui?invitation_id=test-id-123" + assert result == "http://test.com/ui/onboarding?invitation_id=test-id-123" @pytest.mark.asyncio @@ -408,7 +410,10 @@ async def test_get_invitation_link_creates_new_when_none_exist(base_email_logger assert call_args["user_api_key_dict"].user_id == "test-user" # Verify the returned link uses the new invitation ID - assert result == "http://test.com/ui?invitation_id=new-invitation-id" + assert ( + result + == "http://test.com/ui/onboarding?invitation_id=new-invitation-id" + ) @pytest.mark.asyncio @@ -439,7 +444,10 @@ async def test_get_invitation_link_uses_existing_when_available(base_email_logge mock_create_invitation.assert_not_called() # Verify the returned link uses the existing invitation ID - assert result == "http://test.com/ui?invitation_id=existing-invitation-id" + assert ( + result + == "http://test.com/ui/onboarding?invitation_id=existing-invitation-id" + ) @pytest.mark.asyncio @@ -475,7 +483,10 @@ async def test_get_invitation_link_creates_new_when_list_is_none(base_email_logg assert call_args["user_api_key_dict"].user_id == "test-user" # Verify the returned link uses the new invitation ID - assert result == "http://test.com/ui?invitation_id=new-invitation-from-none" + assert ( + result + == "http://test.com/ui/onboarding?invitation_id=new-invitation-from-none" + ) @pytest.mark.asyncio @@ -495,7 +506,7 @@ async def test_get_email_params_user_invitation( with mock.patch.object( base_email_logger, "_get_invitation_link", - return_value="http://test.com/ui?invitation_id=test-id", + return_value="http://test.com/ui/onboarding?invitation_id=test-id", ): # Test with user invitation event result = await base_email_logger._get_email_params( @@ -509,7 +520,9 @@ async def test_get_email_params_user_invitation( == "https://litellm-listing.s3.amazonaws.com/litellm_logo.png" ) assert result.support_contact == "support@berri.ai" - assert result.base_url == "http://test.com/ui?invitation_id=test-id" + assert ( + result.base_url == "http://test.com/ui/onboarding?invitation_id=test-id" + ) assert result.recipient_email == "test@example.com" diff --git a/ui/litellm-dashboard/src/components/bulk_create_users_button.tsx b/ui/litellm-dashboard/src/components/bulk_create_users_button.tsx index 17690c988dc..23b69722546 100644 --- a/ui/litellm-dashboard/src/components/bulk_create_users_button.tsx +++ b/ui/litellm-dashboard/src/components/bulk_create_users_button.tsx @@ -361,7 +361,7 @@ const BulkCreateUsersButton: React.FC = ({ if (!uiSettings?.SSO_ENABLED) { // Regular invitation flow const invitationData = await invitationCreateCall(accessToken, user_id); - const invitationUrl = new URL(`/ui?invitation_id=${invitationData.id}`, baseUrl).toString(); + const invitationUrl = new URL(`/ui/onboarding?invitation_id=${invitationData.id}`, baseUrl).toString(); setParsedData((current) => current.map((u, i) => diff --git a/ui/litellm-dashboard/src/components/onboarding_link.test.tsx b/ui/litellm-dashboard/src/components/onboarding_link.test.tsx new file mode 100644 index 00000000000..039d5e250da --- /dev/null +++ b/ui/litellm-dashboard/src/components/onboarding_link.test.tsx @@ -0,0 +1,70 @@ +import { describe, it, expect } from "vitest"; +import { buildOnboardingUrl } from "./onboarding_link"; + +describe("buildOnboardingUrl", () => { + it("points the invitation link at the dedicated /ui/onboarding route", () => { + expect( + buildOnboardingUrl({ + baseUrl: "http://localhost:4000/", + invitationId: "inv-123", + hasUserSetupSso: false, + resetPassword: false, + }), + ).toBe("http://localhost:4000/ui/onboarding?invitation_id=inv-123"); + }); + + it("preserves a server_root_path prefix before /ui/onboarding", () => { + expect( + buildOnboardingUrl({ + baseUrl: "https://proxy.example.com/litellm", + invitationId: "inv-123", + hasUserSetupSso: false, + resetPassword: false, + }), + ).toBe("https://proxy.example.com/litellm/ui/onboarding?invitation_id=inv-123"); + }); + + it("appends action=reset_password for the reset-password flow", () => { + expect( + buildOnboardingUrl({ + baseUrl: "http://localhost:4000/", + invitationId: "inv-123", + hasUserSetupSso: false, + resetPassword: true, + }), + ).toBe("http://localhost:4000/ui/onboarding?invitation_id=inv-123&action=reset_password"); + }); + + it("sends SSO users to the dashboard root, not the onboarding form", () => { + expect( + buildOnboardingUrl({ + baseUrl: "http://localhost:4000/", + invitationId: "inv-123", + hasUserSetupSso: true, + resetPassword: false, + }), + ).toBe("http://localhost:4000/ui"); + }); + + it("returns an empty string when no base URL is known yet", () => { + expect( + buildOnboardingUrl({ + baseUrl: "", + invitationId: "inv-123", + hasUserSetupSso: false, + resetPassword: false, + }), + ).toBe(""); + }); + + it("returns an empty string rather than an invitation_id=undefined link when the id is not ready", () => { + expect( + buildOnboardingUrl({ + baseUrl: "http://localhost:4000/", + invitationId: undefined, + hasUserSetupSso: false, + resetPassword: false, + }), + ).toBe(""); + }); +}); diff --git a/ui/litellm-dashboard/src/components/onboarding_link.tsx b/ui/litellm-dashboard/src/components/onboarding_link.tsx index 7eb337a970a..0c27287a9e4 100644 --- a/ui/litellm-dashboard/src/components/onboarding_link.tsx +++ b/ui/litellm-dashboard/src/components/onboarding_link.tsx @@ -25,6 +25,32 @@ interface OnboardingProps { modalType?: "invitation" | "resetPassword"; } +export function buildOnboardingUrl({ + baseUrl, + invitationId, + hasUserSetupSso, + resetPassword, +}: { + baseUrl: string; + invitationId: string | undefined; + hasUserSetupSso: boolean; + resetPassword: boolean; +}): string { + if (!baseUrl) { + return ""; + } + const basePath = new URL(baseUrl).pathname; + const uiPath = basePath && basePath !== "/" ? `${basePath}/ui` : "ui"; + if (hasUserSetupSso) { + return new URL(uiPath, baseUrl).toString(); + } + if (!invitationId) { + return ""; + } + const action = resetPassword ? "&action=reset_password" : ""; + return new URL(`${uiPath}/onboarding?invitation_id=${invitationId}${action}`, baseUrl).toString(); +} + export default function OnboardingModal({ isInvitationLinkModalVisible, setIsInvitationLinkModalVisible, @@ -41,24 +67,13 @@ export default function OnboardingModal({ setIsInvitationLinkModalVisible(false); }; - const getInvitationUrl = () => { - if (!baseUrl) { - return ""; - } - const baseUrlObj = new URL(baseUrl); - const basePath = baseUrlObj.pathname; // This will be "/litellm" or "" - const path = basePath && basePath !== "/" ? `${basePath}/ui` : "ui"; - // Get the path from the base URL - if (invitationLinkData?.has_user_setup_sso) { - return new URL(path, baseUrl).toString(); - } - let urlPath = `${path}?invitation_id=${invitationLinkData?.id}`; - if (modalType === "resetPassword") { - urlPath += "&action=reset_password"; - } - const url = new URL(urlPath, baseUrl).toString(); - return url; - }; + const getInvitationUrl = () => + buildOnboardingUrl({ + baseUrl, + invitationId: invitationLinkData?.id, + hasUserSetupSso: invitationLinkData?.has_user_setup_sso ?? false, + resetPassword: modalType === "resetPassword", + }); return ( Date: Wed, 8 Jul 2026 20:54:40 -0700 Subject: [PATCH 037/399] fix(bedrock): honor cache_control ttl on message-level cachePoint blocks (#32551) Bedrock Converse supports cachePoint ttl (1h GA for Claude 4.5+), and _get_cache_point_block maps cache_control.ttl -> cachePoint.ttl, but the model parameter its allow-list gate requires was only threaded through the system-message path. Every message-level path either called _get_cache_point_block without model= (8 call sites in _bedrock_converse_messages_pt / _pt_async) or hardcoded CachePointBlock(type="default") (tool-result blocks and _convert_to_bedrock_tool_call_invoke), so a requested 1h ttl silently degraded to the 5-minute default - exactly on the conversation-tail breakpoint that long-running agents need to survive tool calls longer than 5 minutes. - pass model= at the 8 _get_cache_point_block call sites - tool-result blocks: capture the cache_control dict (was a boolean) and route through _get_cache_point_block so ttl survives - _convert_to_bedrock_tool_call_invoke: accept optional model and route per-tool-call cache_control through _get_cache_point_block Completes the ttl support added for system messages (#19848, #20326): message-level cache_control now behaves identically. Note: message-level cache_control on a content-less assistant message emits no cachePoint at all today; that pre-existing gap is orthogonal to ttl and left out of scope (per-tool-call placement covers it). Co-authored-by: Arash --- .../prompt_templates/factory.py | 56 +++++++---- .../chat/test_converse_transformation.py | 96 +++++++++++++++++++ 2 files changed, 135 insertions(+), 17 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 06abb591717..8bb0e12905e 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -3626,6 +3626,7 @@ class BedrockImageProcessor: def _convert_to_bedrock_tool_call_invoke( tool_calls: list, + model: Optional[str] = None, ) -> List[BedrockContentBlock]: """ OpenAI tool invokes: @@ -3701,7 +3702,13 @@ def _convert_to_bedrock_tool_call_invoke( # cache_control applies to the whole original # tool call; attach after the last split block. if tool.get("cache_control", None) is not None: - _parts_list.append(BedrockContentBlock(cachePoint=CachePointBlock(type="default"))) + _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool["cache_control"]}, + block_type="content_block", + model=model, + ) + if _cache_point_block is not None: + _parts_list.append(_cache_point_block) continue # Fallback: no objects extracted — use empty dict. arguments_dict = {} @@ -3712,8 +3719,13 @@ def _convert_to_bedrock_tool_call_invoke( # Check for cache_control and add a separate cachePoint block if tool.get("cache_control", None) is not None: - cache_point_block = BedrockContentBlock(cachePoint=CachePointBlock(type="default")) - _parts_list.append(cache_point_block) + cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool["cache_control"]}, + block_type="content_block", + model=model, + ) + if cache_point_block is not None: + _parts_list.append(cache_point_block) return _parts_list except Exception as e: raise Exception( @@ -4417,22 +4429,27 @@ class BedrockConverseMessagesProcessor: tool_content.append(tool_call_result) # Check if we need to add a separate cachePoint block - has_cache_control = False + tool_msg_cache_control = None # Check for message-level cache_control if current_message.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = current_message["cache_control"] # Check for content-level cache_control in list content elif isinstance(current_message.get("content"), list): for content_element in current_message["content"]: if isinstance(content_element, dict) and content_element.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = content_element["cache_control"] break # Add a separate cachePoint block if cache_control is present - if has_cache_control: - cache_point_block = BedrockContentBlock(cachePoint=CachePointBlock(type="default")) - tool_content.append(cache_point_block) + if tool_msg_cache_control is not None: + cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool_msg_cache_control}, + block_type="content_block", + model=model, + ) + if cache_point_block is not None: + tool_content.append(cache_point_block) msg_i += 1 # Deduplicate toolResult blocks with the same toolUseId @@ -4529,7 +4546,7 @@ class BedrockConverseMessagesProcessor: _tool_calls = assistant_message_block.get("tool_calls", []) if _tool_calls: - assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls)) + assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls, model=model)) msg_i += 1 @@ -4789,22 +4806,27 @@ def _bedrock_converse_messages_pt( tool_content.append(tool_call_result) # Check if we need to add a separate cachePoint block - has_cache_control = False + tool_msg_cache_control = None # Check for message-level cache_control if current_message.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = current_message["cache_control"] # Check for content-level cache_control in list content elif isinstance(current_message.get("content"), list): for content_element in current_message["content"]: if isinstance(content_element, dict) and content_element.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = content_element["cache_control"] break # Add a separate cachePoint block if cache_control is present - if has_cache_control: - cache_point_block = BedrockContentBlock(cachePoint=CachePointBlock(type="default")) - tool_content.append(cache_point_block) + if tool_msg_cache_control is not None: + cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool_msg_cache_control}, + block_type="content_block", + model=model, + ) + if cache_point_block is not None: + tool_content.append(cache_point_block) msg_i += 1 # Deduplicate toolResult blocks with the same toolUseId @@ -4902,7 +4924,7 @@ def _bedrock_converse_messages_pt( assistant_content.append(_cache_point_block) _tool_calls = assistant_message_block.get("tool_calls", []) if _tool_calls: - assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls)) + assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls, model=model)) msg_i += 1 diff --git a/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py b/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py index fe060e2e40d..9f2a4168dec 100644 --- a/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py +++ b/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py @@ -5671,3 +5671,99 @@ async def test_grounding_source_and_query_rendered_as_text(): user_content = result[0]["content"] assert {"text": "Tokyo is the capital of Japan."} in user_content assert {"text": "What is the capital of Japan?"} in user_content + + +def _agentic_messages_with_ttl(ttl_target: str): + """A tool-loop conversation with `ttl: 1h` cache_control at `ttl_target`: + 'user', 'tool_call' (per-tool-call, on the assistant's tool call), or + 'tool' (message-level, on the tool result - where + `cache_control_injection_points` with `index: -1` lands mid-loop). + + Message-level cache_control on a content-less assistant message emits no + cachePoint at all today (a separate gap, orthogonal to ttl); per-tool-call + placement covers that message, so it's excluded from the params below.""" + user: dict = {"role": "user", "content": "optimize this kernel " * 60} + assistant: dict = { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "evaluate", "arguments": "{}"}, + } + ], + } + tool: dict = {"role": "tool", "tool_call_id": "call_1", "content": "score: 42"} + ttl_cc = {"type": "ephemeral", "ttl": "1h"} + if ttl_target == "user": + user["cache_control"] = ttl_cc + elif ttl_target == "tool_call": + assistant["tool_calls"][0]["cache_control"] = ttl_cc + elif ttl_target == "tool": + tool["cache_control"] = ttl_cc + return [user, assistant, tool] + + +def _collect_cache_points(result): + return [ + block["cachePoint"] + for message in result + for block in message.get("content") or [] + if "cachePoint" in block + ] + + +@pytest.mark.parametrize("ttl_target", ["user", "tool_call", "tool"]) +@pytest.mark.asyncio +async def test_message_level_cache_control_honors_ttl_for_supported_model( + ttl_target, +): + """Message- and tool-call-level cache_control must carry `ttl` onto the + emitted cachePoint for models that support extended caching, mirroring the + system-message path. Regression test for the gap left by the system-only + fix: the message paths called `_get_cache_point_block` without `model` (or + hardcoded `{"type": "default"}`), silently downgrading 1h to 5m.""" + from litellm.litellm_core_utils.prompt_templates.factory import ( + BedrockConverseMessagesProcessor, + _bedrock_converse_messages_pt, + ) + + messages = _agentic_messages_with_ttl(ttl_target) + + result = _bedrock_converse_messages_pt( + messages=messages, + model="global.anthropic.claude-opus-4-7", + llm_provider="bedrock_converse", + ) + async_result = ( + await BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async( + messages=messages, + model="global.anthropic.claude-opus-4-7", + llm_provider="bedrock_converse", + ) + ) + assert result == async_result + + cache_points = _collect_cache_points(result) + assert len(cache_points) == 1 + assert cache_points[0].get("ttl") == "1h" + + +@pytest.mark.parametrize("ttl_target", ["user", "tool_call", "tool"]) +def test_message_level_cache_control_drops_ttl_for_unsupported_model(ttl_target): + """Models outside the extended-caching allow-list must keep emitting the + plain `{"type": "default"}` cachePoint (Bedrock rejects `ttl` for them).""" + from litellm.litellm_core_utils.prompt_templates.factory import ( + _bedrock_converse_messages_pt, + ) + + result = _bedrock_converse_messages_pt( + messages=_agentic_messages_with_ttl(ttl_target), + model="anthropic.claude-3-5-sonnet-20240620-v1:0", + llm_provider="bedrock_converse", + ) + + cache_points = _collect_cache_points(result) + assert len(cache_points) == 1 + assert "ttl" not in cache_points[0] From b4d63c1c9fd85eed13b1c7f47f311005c94f187b Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 8 Jul 2026 22:36:13 -0700 Subject: [PATCH 038/399] ci: drop regex file guard from OSS daily guardrails The in-workflow regex list was hard to maintain and, because it runs on pull_request, could be modified by the same PR it inspects. Path gating for the OSS daily branches now lives in repository branch protection settings, so this workflow keeps only the OSS-safe checks: the hardcoded-secret test and ruff --- .github/workflows/oss_daily_guardrails.yml | 59 ---------------------- 1 file changed, 59 deletions(-) diff --git a/.github/workflows/oss_daily_guardrails.yml b/.github/workflows/oss_daily_guardrails.yml index 173b7cd4e41..950c51c9b60 100644 --- a/.github/workflows/oss_daily_guardrails.yml +++ b/.github/workflows/oss_daily_guardrails.yml @@ -17,65 +17,6 @@ concurrency: cancel-in-progress: true jobs: - sensitive-file-guard: - name: Block sensitive OSS daily changes - if: startsWith(github.ref_name, 'litellm_oss_daily_20') || startsWith(github.head_ref, 'litellm_oss_daily_20') || startsWith(github.base_ref, 'litellm_oss_daily_20') - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Check for sensitive file changes - env: - EVENT_NAME: ${{ github.event_name }} - BASE_REF_NAME: ${{ github.base_ref }} - HEAD_REF_NAME: ${{ github.head_ref }} - run: | - set -euo pipefail - - if [ "${EVENT_NAME}" = "pull_request" ] && echo "${HEAD_REF_NAME}" | grep -Eq '^litellm_oss_daily_20[0-9]{2}_[0-9]{2}_[0-9]{2}$'; then - # Final daily OSS branch PR into staging: review only the OSS delta - # accumulated on top of main, not unrelated main/staging drift. - BASE_REF="origin/main" - git fetch origin main - elif [ "${EVENT_NAME}" = "pull_request" ]; then - # PR targeting the daily OSS branch: review the incoming PR delta. - BASE_REF="origin/${BASE_REF_NAME}" - git fetch origin "${BASE_REF_NAME}" - else - # Push to the daily OSS branch: review the accumulated OSS delta. - BASE_REF="origin/main" - git fetch origin main - fi - - CHANGED_FILES="$(git diff --name-only "${BASE_REF}...HEAD")" - - if [ -z "${CHANGED_FILES}" ]; then - echo "No changed files detected." - exit 0 - fi - - echo "Changed files:" - echo "${CHANGED_FILES}" - - BLOCKED_FILES="$( - echo "${CHANGED_FILES}" | grep -E '(^\.github/workflows/|^\.github/actions/|^\.circleci/|^\.cursor/|^Dockerfile$|(^|/)Dockerfile$|^Makefile$|(^|/)Makefile$|(^|/)pyproject\.toml$|(^|/)uv\.lock$|(^|/)poetry\.lock$|(^|/)requirements[^/]*\.txt$|(^|/)package\.json$|(^|/)package-lock\.json$|(^|/)pnpm-lock\.yaml$|(^|/)yarn\.lock$|(^|/)tsconfig[^/]*\.json$|(^|/)(ruff|mypy|pytest|eslint|prettier|vitest|next|vite|jest)\.config\.)' || true - )" - - if [ -n "${BLOCKED_FILES}" ]; then - echo "::error::OSS daily branch contains sensitive workflow, config, dependency, or lockfile changes. Split these into a separate internal/security-reviewed PR." - echo "${BLOCKED_FILES}" - exit 1 - fi - - echo "No sensitive OSS daily file changes detected." - oss-safe-checks: name: Run OSS daily safe checks if: startsWith(github.ref_name, 'litellm_oss_daily_20') || startsWith(github.head_ref, 'litellm_oss_daily_20') || startsWith(github.base_ref, 'litellm_oss_daily_20') From 9813c4bf41b5b9b3af56c8f1dd4aa748bd98713b Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Thu, 9 Jul 2026 11:58:29 +1000 Subject: [PATCH 039/399] feat(ui): add session id filter to request logs --- .../spend_management_endpoints.py | 8 ++ .../test_spend_management_endpoints.py | 84 +++++++++++++++++++ .../src/components/networking.tsx | 1 + .../components/view_logs/filter_options.ts | 5 ++ .../view_logs/log_filter_logic.test.tsx | 1 + .../components/view_logs/log_filter_logic.tsx | 4 + ui/litellm-dashboard/src/lib/http/schema.d.ts | 4 + 7 files changed, 107 insertions(+) diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index 8f530e3b8ce..cf7bedfdc71 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -1622,6 +1622,10 @@ async def ui_view_spend_logs( default=None, description="request_id to get spend logs for specific request_id", ), + session_id: str | None = fastapi.Query( + default=None, + description="Filter spend logs by session_id", + ), team_id: str | None = fastapi.Query( default=None, description="Filter spend logs by team_id", @@ -1772,6 +1776,9 @@ async def ui_view_spend_logs( if request_id is not None: where_conditions["request_id"] = request_id + if session_id is not None: + where_conditions["session_id"] = session_id + if model is not None: where_conditions["model"] = model @@ -1887,6 +1894,7 @@ async def ui_view_spend_logs( ('"user"', "user"), ("api_key", "api_key"), ("request_id", "request_id"), + ("session_id", "session_id"), ("model", "model"), ("model_id", "model_id"), ("model_group", "model_group"), diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py b/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py index 1e9818534c9..69ffc2275ff 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py @@ -1,4 +1,5 @@ import asyncio +import collections import datetime import json import os @@ -85,6 +86,7 @@ def _reconstruct_ui_where_from_sql(sql_query, params): '"user"': "user", "api_key": "api_key", "request_id": "request_id", + "session_id": "session_id", "model": "model", "model_id": "model_id", "model_group": "model_group", @@ -162,7 +164,21 @@ def make_ui_spend_logs_mock_prisma(mock_spend_logs, filter_fn, team_lookup_fn=No async def count(self, *args, **kwargs): return len(filter_fn(kwargs.get("where", {}))) + async def group_by(self, by, where, count): + allowed = set(where["session_id"]["in"]) + tallied = collections.Counter( + log["session_id"] + for log in mock_spend_logs + if log.get("session_id") in allowed + ) + return [ + {"session_id": sid, "_count": {"session_id": n}} + for sid, n in tallied.items() + ] + async def query_raw(self, sql_query, *params): + if "mcp_tool_call_count" in sql_query: + return [] filtered = filter_fn(_reconstruct_ui_where_from_sql(sql_query, params)) total = len(filtered) if "COUNT(*)" in sql_query: @@ -597,6 +613,74 @@ async def test_ui_view_spend_logs_with_user_id(client, monkeypatch): assert data["data"][0]["user"] == "test_user_1" +@pytest.mark.asyncio +async def test_ui_view_spend_logs_with_session_id(client, monkeypatch): + mock_spend_logs = [ + { + "id": "log1", + "request_id": "req1", + "api_key": "sk-test-key", + "user": "test_user_1", + "session_id": "session-abc", + "spend": 0.05, + "startTime": datetime.datetime.now(timezone.utc).isoformat(), + "model": "gpt-4", + }, + { + "id": "log2", + "request_id": "req2", + "api_key": "sk-test-key", + "user": "test_user_1", + "session_id": "session-abc", + "spend": 0.10, + "startTime": datetime.datetime.now(timezone.utc).isoformat(), + "model": "gpt-4", + }, + { + "id": "log3", + "request_id": "req3", + "api_key": "sk-test-key", + "user": "test_user_1", + "session_id": "session-other", + "spend": 0.02, + "startTime": datetime.datetime.now(timezone.utc).isoformat(), + "model": "gpt-4", + }, + ] + + def filter_by_session(where): + if "session_id" in where: + return [ + log + for log in mock_spend_logs + if log["session_id"] == where["session_id"] + ] + return mock_spend_logs + + monkeypatch.setattr( + "litellm.proxy.proxy_server.prisma_client", + make_ui_spend_logs_mock_prisma(mock_spend_logs, filter_by_session), + ) + + start_date, end_date = _default_date_range() + + response = client.get( + "/spend/logs/ui", + params={ + "session_id": "session-abc", + "start_date": start_date, + "end_date": end_date, + }, + headers={"Authorization": "Bearer sk-test"}, + ) + + assert response.status_code == 200 + data = response.json() + assert data["total"] == 2 + assert {log["request_id"] for log in data["data"]} == {"req1", "req2"} + assert all(log["session_id"] == "session-abc" for log in data["data"]) + + # Mock spend logs with distinct values for sorting tests. # req_a: spend=0.10, tokens=500, start/end earliest # req_b: spend=0.05, tokens=200, start/end 2nd diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 403647106d4..5ec2765c621 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -1929,6 +1929,7 @@ interface UiSpendLogsParams { api_key?: string; team_id?: string; request_id?: string; + session_id?: string; user_id?: string; end_user?: string; status_filter?: string; diff --git a/ui/litellm-dashboard/src/components/view_logs/filter_options.ts b/ui/litellm-dashboard/src/components/view_logs/filter_options.ts index 52632ea5861..90e27b6144d 100644 --- a/ui/litellm-dashboard/src/components/view_logs/filter_options.ts +++ b/ui/litellm-dashboard/src/components/view_logs/filter_options.ts @@ -63,6 +63,11 @@ export function getLogFilterOptions(accessToken: string): FilterOption[] { label: "Key Hash", isSearchable: false, }, + { + name: FILTER_KEYS.SESSION_ID, + label: "Session ID", + isSearchable: false, + }, { name: "Model", label: "Model", diff --git a/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.test.tsx b/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.test.tsx index cbe37e0b70f..ef550baea91 100644 --- a/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/log_filter_logic.test.tsx @@ -218,6 +218,7 @@ describe("useLogFilterLogic", () => { { filterKey: "Team ID", paramName: "team_id", value: "team-a" }, { filterKey: "Key Hash", paramName: "api_key", value: "key-x" }, { filterKey: "Request ID", paramName: "request_id", value: "req-xyz" }, + { filterKey: "Session ID", paramName: "session_id", value: "sess-42" }, { filterKey: "User ID", paramName: "user_id", value: "user-123" }, { filterKey: "End User", paramName: "end_user", value: "user-a" }, { filterKey: "Status", paramName: "status_filter", value: "error" }, 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 c45e03905d0..1d699042f05 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 @@ -30,6 +30,7 @@ export const FILTER_KEYS = { TEAM_ID: "Team ID", KEY_HASH: "Key Hash", REQUEST_ID: "Request ID", + SESSION_ID: "Session 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", @@ -49,6 +50,7 @@ const TEXT_FILTER_KEYS: readonly (keyof LogFilterState)[] = [ FILTER_KEYS.KEY_HASH, FILTER_KEYS.ERROR_MESSAGE, FILTER_KEYS.REQUEST_ID, + FILTER_KEYS.SESSION_ID, FILTER_KEYS.USER_ID, FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL, ]; @@ -62,6 +64,7 @@ export const defaultFilters: LogFilterState = { [FILTER_KEYS.TEAM_ID]: "", [FILTER_KEYS.KEY_HASH]: "", [FILTER_KEYS.REQUEST_ID]: "", + [FILTER_KEYS.SESSION_ID]: "", [FILTER_KEYS.MODEL]: "", [FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL]: "", [FILTER_KEYS.USER_ID]: "", @@ -160,6 +163,7 @@ export function useLogFilterLogic({ api_key: effectiveFilters[FILTER_KEYS.KEY_HASH] || undefined, team_id: effectiveFilters[FILTER_KEYS.TEAM_ID] || undefined, request_id: effectiveFilters[FILTER_KEYS.REQUEST_ID] || undefined, + session_id: effectiveFilters[FILTER_KEYS.SESSION_ID] || undefined, user_id: effectiveFilters[FILTER_KEYS.USER_ID] || (filterByCurrentUser ? userID ?? undefined : undefined), end_user: effectiveFilters[FILTER_KEYS.END_USER] || undefined, status_filter: effectiveFilters[FILTER_KEYS.STATUS] || undefined, diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 852f8388ec2..8e26729aa18 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -48633,6 +48633,8 @@ export interface operations { user_id?: string | null; /** @description request_id to get spend logs for specific request_id */ request_id?: string | null; + /** @description Filter spend logs by session_id */ + session_id?: string | null; /** @description Filter spend logs by team_id */ team_id?: string | null; /** @description Filter logs with spend greater than or equal to this value */ @@ -48739,6 +48741,8 @@ export interface operations { user_id?: string | null; /** @description request_id to get spend logs for specific request_id */ request_id?: string | null; + /** @description Filter spend logs by session_id */ + session_id?: string | null; /** @description Filter spend logs by team_id */ team_id?: string | null; /** @description Filter logs with spend greater than or equal to this value */ From f33403cb4b3b4122eadb2bb628ff7b99d8f5be02 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Thu, 9 Jul 2026 12:39:13 +1000 Subject: [PATCH 040/399] feat(ui): support partial match on session id filter --- .../spend_management_endpoints.py | 12 ++- .../test_spend_management_endpoints.py | 91 +++++++++---------- ui/litellm-dashboard/src/lib/http/schema.d.ts | 4 +- 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index cf7bedfdc71..0bf35352d5f 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -1624,7 +1624,7 @@ async def ui_view_spend_logs( ), session_id: str | None = fastapi.Query( default=None, - description="Filter spend logs by session_id", + description="Filter spend logs by session_id (partial string match)", ), team_id: str | None = fastapi.Query( default=None, @@ -1776,9 +1776,6 @@ async def ui_view_spend_logs( if request_id is not None: where_conditions["request_id"] = request_id - if session_id is not None: - where_conditions["session_id"] = session_id - if model is not None: where_conditions["model"] = model @@ -1894,7 +1891,6 @@ async def ui_view_spend_logs( ('"user"', "user"), ("api_key", "api_key"), ("request_id", "request_id"), - ("session_id", "session_id"), ("model", "model"), ("model_id", "model_id"), ("model_group", "model_group"), @@ -1914,6 +1910,12 @@ async def ui_view_spend_logs( p += 2 sql_conditions.append(or_clause) + if session_id is not None: + like_escaped_session_id = session_id.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") + sql_conditions.append(f"session_id LIKE ${p}") + sql_params.append(f"%{like_escaped_session_id}%") + p += 1 + # Status filter if status_filter is not None: if status_filter == "success": diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py b/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py index 69ffc2275ff..c3899883d58 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py @@ -86,7 +86,6 @@ def _reconstruct_ui_where_from_sql(sql_query, params): '"user"': "user", "api_key": "api_key", "request_id": "request_id", - "session_id": "session_id", "model": "model", "model_id": "model_id", "model_group": "model_group", @@ -100,6 +99,7 @@ def _reconstruct_ui_where_from_sql(sql_query, params): alias = re.search(r"user_api_key_alias' LIKE \$(\d+)", cond) code = re.search(r"error_code' = \$(\d+)", cond) msg = re.search(r"error_message' LIKE \$(\d+)", cond) + sess = re.fullmatch(r"session_id LIKE \$(\d+)", cond) status = re.fullmatch(r"status = \$(\d+)", cond) if gte: date_bounds["gte"] = _iso(params[int(gte.group(1)) - 1]) @@ -109,6 +109,8 @@ def _reconstruct_ui_where_from_sql(sql_query, params): where["OR"] = where.get("OR", []) + [{"multi_team": True}] elif "status = 'success'" in cond: where["OR"] = where.get("OR", []) + [{"status": "success"}] + elif sess: + where["session_id"] = {"contains": str(params[int(sess.group(1)) - 1]).strip("%")} elif status: where["status"] = {"equals": params[int(status.group(1)) - 1]} elif alias: @@ -165,16 +167,14 @@ def make_ui_spend_logs_mock_prisma(mock_spend_logs, filter_fn, team_lookup_fn=No return len(filter_fn(kwargs.get("where", {}))) async def group_by(self, by, where, count): - allowed = set(where["session_id"]["in"]) + col = by[0] + allowed = where.get(col, {}).get("in") tallied = collections.Counter( - log["session_id"] + log[col] for log in mock_spend_logs - if log.get("session_id") in allowed + if log.get(col) is not None and (allowed is None or log[col] in allowed) ) - return [ - {"session_id": sid, "_count": {"session_id": n}} - for sid, n in tallied.items() - ] + return [{col: value, "_count": {col: n}} for value, n in tallied.items()] async def query_raw(self, sql_query, *params): if "mcp_tool_call_count" in sql_query: @@ -614,48 +614,47 @@ async def test_ui_view_spend_logs_with_user_id(client, monkeypatch): @pytest.mark.asyncio -async def test_ui_view_spend_logs_with_session_id(client, monkeypatch): - mock_spend_logs = [ - { - "id": "log1", - "request_id": "req1", +@pytest.mark.parametrize( + "session_id_query,expected_request_ids", + [ + ("session-filter-demo-1", {"req1", "req2"}), + ("session-filter-demo-2", {"req3"}), + ("session-filter", {"req1", "req2", "req3"}), + ("demo", {"req1", "req2", "req3"}), + ("no-such-session", set()), + ], +) +async def test_ui_view_spend_logs_with_session_id( + client, monkeypatch, session_id_query, expected_request_ids +): + def make_log(request_id, session_id): + return { + "id": f"log-{request_id}", + "request_id": request_id, "api_key": "sk-test-key", "user": "test_user_1", - "session_id": "session-abc", + "session_id": session_id, "spend": 0.05, "startTime": datetime.datetime.now(timezone.utc).isoformat(), "model": "gpt-4", - }, - { - "id": "log2", - "request_id": "req2", - "api_key": "sk-test-key", - "user": "test_user_1", - "session_id": "session-abc", - "spend": 0.10, - "startTime": datetime.datetime.now(timezone.utc).isoformat(), - "model": "gpt-4", - }, - { - "id": "log3", - "request_id": "req3", - "api_key": "sk-test-key", - "user": "test_user_1", - "session_id": "session-other", - "spend": 0.02, - "startTime": datetime.datetime.now(timezone.utc).isoformat(), - "model": "gpt-4", - }, + } + + mock_spend_logs = [ + make_log("req1", "session-filter-demo-1"), + make_log("req2", "session-filter-demo-1"), + make_log("req3", "session-filter-demo-2"), + make_log("req4", "unrelated-abc"), ] def filter_by_session(where): - if "session_id" in where: - return [ - log - for log in mock_spend_logs - if log["session_id"] == where["session_id"] - ] - return mock_spend_logs + session_filter = where.get("session_id") + if session_filter is None: + return mock_spend_logs + return [ + log + for log in mock_spend_logs + if session_filter["contains"] in log["session_id"] + ] monkeypatch.setattr( "litellm.proxy.proxy_server.prisma_client", @@ -667,7 +666,7 @@ async def test_ui_view_spend_logs_with_session_id(client, monkeypatch): response = client.get( "/spend/logs/ui", params={ - "session_id": "session-abc", + "session_id": session_id_query, "start_date": start_date, "end_date": end_date, }, @@ -676,9 +675,9 @@ async def test_ui_view_spend_logs_with_session_id(client, monkeypatch): assert response.status_code == 200 data = response.json() - assert data["total"] == 2 - assert {log["request_id"] for log in data["data"]} == {"req1", "req2"} - assert all(log["session_id"] == "session-abc" for log in data["data"]) + assert data["total"] == len(expected_request_ids) + assert {log["request_id"] for log in data["data"]} == expected_request_ids + assert all(session_id_query in log["session_id"] for log in data["data"]) # Mock spend logs with distinct values for sorting tests. diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 8e26729aa18..4ca2f85be2b 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -48633,7 +48633,7 @@ export interface operations { user_id?: string | null; /** @description request_id to get spend logs for specific request_id */ request_id?: string | null; - /** @description Filter spend logs by session_id */ + /** @description Filter spend logs by session_id (partial string match) */ session_id?: string | null; /** @description Filter spend logs by team_id */ team_id?: string | null; @@ -48741,7 +48741,7 @@ export interface operations { user_id?: string | null; /** @description request_id to get spend logs for specific request_id */ request_id?: string | null; - /** @description Filter spend logs by session_id */ + /** @description Filter spend logs by session_id (partial string match) */ session_id?: string | null; /** @description Filter spend logs by team_id */ team_id?: string | null; From 7801324ab0c06b1997010c14aed18b6a2d8fdce4 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Thu, 9 Jul 2026 15:33:11 +1000 Subject: [PATCH 041/399] fix(spend): guard session_id filter against non-str query default --- litellm/proxy/spend_tracking/spend_management_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index 0bf35352d5f..bce2e3581b5 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -1910,7 +1910,7 @@ async def ui_view_spend_logs( p += 2 sql_conditions.append(or_clause) - if session_id is not None: + if session_id is not None and isinstance(session_id, str): like_escaped_session_id = session_id.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") sql_conditions.append(f"session_id LIKE ${p}") sql_params.append(f"%{like_escaped_session_id}%") From 8a44fdd66321f26d87223112487e72e0a8c24e27 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Thu, 9 Jul 2026 15:53:13 +1000 Subject: [PATCH 042/399] chore(ui): refresh eslint metrics for rebased base --- ui/litellm-dashboard/eslint-metrics.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index ded6ab97e1e..37bad071081 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,7 +1,7 @@ { "@typescript-eslint/no-explicit-any": 1982, "complexity": 128, - "local/no-large-inline-object-arg": 512, + "local/no-large-inline-object-arg": 519, "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 From e84a19acd566f6ac95ec6346ba603104adea728f Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Wed, 8 Jul 2026 23:24:11 -0700 Subject: [PATCH 043/399] fix(guardrails): walk Responses-API text taxonomy in shared content helpers (#32542) * fix(guardrails): walk Responses-API text taxonomy in shared content helpers Every guardrail sharing litellm/proxy/guardrails/_content_utils.py silently drops all text on the /v1/responses path. AIM turns it into a loud 422 ( {"error":"No messages in the request"}); every other guardrail (Lakera v2, Cato, Lasso, Repello, IBM, Azure Content Safety, enterprise secret detection) scans an empty payload and lets the request through unscanned. Three defects, all in _content_utils.py: 1. _iter_text_parts_in_content recognised only part.type == "text", but the Responses API uses input_text (request) and output_text (assistant). 2. _coerce_input_to_messages gated on "every item has a role key"; any Responses input list containing a function_call or function_call_output item failed the check and was wrapped as one opaque blob. 3. build_inspection_messages forwarded any role through, including a bare tool role missing tool_call_id, which validators like AIM's /fw/v1/analyze reject with a schema error. Fix walks the actual Responses item taxonomy (message, function_call, function_call_output, bare content parts and strings), recognises {text, input_text, output_text} everywhere, and coerces any role outside {system, user, assistant} to user in the outbound inspection payload. * style: ruff-format changed guardrail files * test(guardrails): cover function_call_output string form; drop em-dash in new docstring * fix(guardrails): map function_call_output straight to user role Avoids ever materialising a schema-invalid bare tool message. The downstream role-safety coercion in build_inspection_messages still guards genuinely caller-supplied non-standard roles (developer, function, custom values); add a regression test covering that path so the coercion has real coverage after this simplification. * test(guardrails): pin chat-completions tool-role coercion in build_inspection_messages * docs(test): soften AIM-specific claims in LIT-4294 test docstrings Ryan's review flagged that several test docstrings assert AIM's /fw/v1/analyze validates + rejects specific schema violations. That behavior is customer-reported in the LIT-4294 writeup, not directly verified by us. Rephrase to attribute the AIM 422 to the customer's writeup and describe the underlying constraint as the OpenAI chat schema; any downstream API that validates against that schema rejects the same shape. * refactor(guardrails): move unsupported-role coercion into AIM only The generic coercion in build_inspection_messages collapsed any role outside {system, user, assistant} to user for every caller of the helper. Combined with the pre-existing apply_redacted_messages_back write-back behavior in Lakera/AIM/Cato, that turned a loud OpenAI 400 on chat-completions tool-message masking into a silent semantic corruption of the outbound request (role tool with tool_call_id got rewritten to bare role user, dropping the assistant + tool_calls sibling). AIM specifically requires the coercion because its /fw/v1/analyze validates the payload against the OpenAI chat schema; other guardrails either do not validate roles or do their own reconstruction. Move the coercion to AimGuardrail._build_aim_inspection_messages so the shared helper keeps caller roles intact and no new cross-guardrail role corruption is introduced. The pre-existing apply_redacted_messages_back structural flatten remains as separate follow-up work. function_call_output items still synthesise role user in the shared helper because they have no natural role field, which is a different concern from coercing a caller-supplied role. * refactor(guardrails): preserve role fidelity in shared _content_utils Shared inspection helpers should extract text and preserve semantic role signals; role coercion for third-party schema safety stays inside the guardrail that needs it (AIM). Three shared-helper changes: - Bare content-part dicts (input_text/output_text) with an explicit role keep it; only role-less parts default to user. - Responses message items already had their role preserved; the behavior is now covered by an explicit test. - function_call_output items default to role tool (semantic equivalent of the chat-completions tool message shape) instead of role user, so Responses and chat completions produce symmetric inspection payloads. A caller-supplied role on the item is still preserved. AIM's schema-safe coercion in _build_aim_inspection_messages already handles the resulting role tool: it collapses to user before the POST to /fw/v1/analyze so AIM's OpenAI-schema validator does not reject the bare tool message (no tool_call_id can survive the flatten). Added a regression test in test_aim.py covering that path. --- litellm/proxy/guardrails/_content_utils.py | 60 ++--- .../guardrails/guardrail_hooks/aim/aim.py | 17 +- .../guardrails/guardrail_hooks/test_aim.py | 88 +++++++ .../proxy/guardrails/test_content_utils.py | 237 +++++++++++++++++- 4 files changed, 364 insertions(+), 38 deletions(-) create mode 100644 tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py diff --git a/litellm/proxy/guardrails/_content_utils.py b/litellm/proxy/guardrails/_content_utils.py index 1d31e33c77c..6cdf49818e6 100644 --- a/litellm/proxy/guardrails/_content_utils.py +++ b/litellm/proxy/guardrails/_content_utils.py @@ -32,6 +32,9 @@ def is_text_content_call_type(call_type: str) -> bool: return call_type in TEXT_CONTENT_CALL_TYPES +TEXT_PART_TYPES: FrozenSet[str] = frozenset({"text", "input_text", "output_text"}) + + def _iter_text_parts_in_content(content: Any) -> Iterator[str]: """Yield text fragments from a ``message.content`` value (string or multimodal list). Non-text parts (images, audio, …) are skipped.""" @@ -48,7 +51,7 @@ def _iter_text_parts_in_content(content: Any) -> Iterator[str]: continue if not isinstance(part, dict): continue - if part.get("type") == "text": + if part.get("type") in TEXT_PART_TYPES: text = part.get("text") if isinstance(text, str) and text: yield text @@ -58,14 +61,20 @@ def _coerce_input_to_messages(input_value: Any) -> List[Dict[str, Any]]: """Coerce a Responses-API ``data["input"]`` value into chat-style messages.""" if isinstance(input_value, str): return [{"role": "user", "content": input_value}] - if isinstance(input_value, list): - if input_value and all(isinstance(item, dict) and "role" in item for item in input_value): - return list(input_value) - # Mixed lists (content-part dicts + bare strings) and pure - # string/dict lists all become a single user message; the content - # iterator below handles each element type uniformly. - return [{"role": "user", "content": input_value}] - return [] + if not isinstance(input_value, list): + return [] + messages: List[Dict[str, Any]] = [] + for item in input_value: + if isinstance(item, str): + messages.append({"role": "user", "content": item}) + elif isinstance(item, dict): + if item.get("type") in TEXT_PART_TYPES: + messages.append({"role": item.get("role") or "user", "content": [item]}) + elif "content" in item: + messages.append({"role": item.get("role") or "user", "content": item["content"]}) + elif item.get("type") == "function_call_output" and "output" in item: + messages.append({"role": item.get("role") or "tool", "content": item["output"]}) + return messages def _iter_inspection_messages(data: Dict[str, Any]) -> Iterator[Dict[str, Any]]: @@ -112,7 +121,7 @@ def walk_user_text(data: Dict[str, Any], visit: Callable[[str], str]) -> int: new_parts.append(visit(part)) elif ( isinstance(part, dict) - and part.get("type") == "text" + and part.get("type") in TEXT_PART_TYPES and isinstance(part.get("text"), str) and part["text"] ): @@ -136,25 +145,20 @@ def walk_user_text(data: Dict[str, Any], visit: Callable[[str], str]) -> int: data["input"] = visit(input_value) return visited if isinstance(input_value, list): - # List of full messages: rewrite each message's content. - if input_value and all(isinstance(item, dict) and "role" in item for item in input_value): - for item in input_value: - if "content" in item: - item["content"] = _rewrite_content(item["content"]) - return visited - # List of content parts and/or bare strings: rewrite in place. for idx, item in enumerate(input_value): - if isinstance(item, str) and item: - visited += 1 - input_value[idx] = visit(item) - elif ( - isinstance(item, dict) - and item.get("type") == "text" - and isinstance(item.get("text"), str) - and item["text"] - ): - visited += 1 - input_value[idx] = {**item, "text": visit(item["text"])} + if isinstance(item, str): + if item: + visited += 1 + input_value[idx] = visit(item) + elif isinstance(item, dict): + if item.get("type") in TEXT_PART_TYPES: + if isinstance(item.get("text"), str) and item["text"]: + visited += 1 + input_value[idx] = {**item, "text": visit(item["text"])} + elif "content" in item: + item["content"] = _rewrite_content(item["content"]) + elif item.get("type") == "function_call_output" and "output" in item: + item["output"] = _rewrite_content(item["output"]) return visited return visited diff --git a/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py b/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py index e7d9406ae3b..d22243cbe88 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py +++ b/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py @@ -93,11 +93,10 @@ class AimGuardrail(CustomGuardrail): user_email=user_email, litellm_call_id=call_id, ) - # Covers multimodal list content + Responses-API input. response = await self.async_handler.post( f"{self.api_base}/fw/v1/analyze", headers=headers, - json={"messages": build_inspection_messages(data)}, + json={"messages": self._build_aim_inspection_messages(data)}, ) response.raise_for_status() res = response.json() @@ -116,6 +115,15 @@ class AimGuardrail(CustomGuardrail): verbose_proxy_logger.error(f"Aim: {action_type} action") return data + @staticmethod + def _build_aim_inspection_messages(data: dict) -> list[dict[str, str]]: + """AIM validates against the OpenAI chat schema. Bare ``role: "tool"`` + without ``tool_call_id`` and bare ``role: "function"`` without ``name`` + are rejected; the flatten drops those fields, so any role outside + ``{system, user, assistant}`` collapses to ``user`` for the AIM POST.""" + safe_roles = {"system", "user", "assistant"} + return [{**m, "role": "user"} if m["role"] not in safe_roles else m for m in build_inspection_messages(data)] + @staticmethod def _rejection(message: str, *, openai_code: str | None = None) -> ProxyException: return ProxyException( @@ -177,7 +185,10 @@ class AimGuardrail(CustomGuardrail): user_email=user_email, litellm_call_id=call_id, ), - json={"messages": build_inspection_messages(request_data) + [{"role": "assistant", "content": output}]}, + json={ + "messages": self._build_aim_inspection_messages(request_data) + + [{"role": "assistant", "content": output}] + }, ) response.raise_for_status() res = response.json() diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py new file mode 100644 index 00000000000..2e83422074e --- /dev/null +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py @@ -0,0 +1,88 @@ +"""Tests for the AIM guardrail's inspection-payload construction.""" + +from litellm.proxy.guardrails.guardrail_hooks.aim.aim import AimGuardrail + + +def test_aim_inspection_messages_coerces_chat_completions_tool_role_to_user(): + """LIT-4294: A valid chat-completions ``role: "tool"`` message carries a + ``tool_call_id``, but the inspection flatten drops every field except + ``role`` and ``content``. A bare ``tool`` message without ``tool_call_id`` + is schema-invalid per the OpenAI chat schema, and the customer's writeup + reproduced AIM's ``/fw/v1/analyze`` returning 422 on exactly that shape. + The AIM POST collapses the role to ``user``; the outbound request to the + LLM is untouched.""" + data = { + "messages": [ + {"role": "user", "content": "weather in SF"}, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "id": "c1", + "type": "function", + "function": {"name": "get_weather", "arguments": "{}"}, + } + ], + }, + {"role": "tool", "tool_call_id": "c1", "content": "sunny"}, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "user", "content": "weather in SF"}, + {"role": "user", "content": "sunny"}, + ] + + +def test_aim_inspection_messages_coerces_non_standard_caller_role_to_user(): + """LIT-4294: A caller-supplied role outside {system, user, assistant} + (e.g. ``developer``, ``function``) is coerced to ``user`` for the AIM + POST, since AIM validates the payload against the OpenAI chat schema + and rejects unknown roles the same way it rejects bare ``tool``.""" + data = { + "messages": [ + {"role": "developer", "content": "system-ish instruction"}, + {"role": "user", "content": "normal user text"}, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "user", "content": "system-ish instruction"}, + {"role": "user", "content": "normal user text"}, + ] + + +def test_aim_inspection_messages_coerces_responses_function_call_output_role(): + """LIT-4294: the shared helper synthesises ``role: "tool"`` for a + Responses ``function_call_output`` item (semantic equivalent of + chat-completions tool messages). AIM's schema-validating POST cannot + carry ``tool_call_id`` in the flat inspection payload, so AIM collapses + that ``tool`` role to ``user`` locally before POSTing.""" + data = { + "input": [ + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "sunny"}], + }, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "user", "content": "sunny"}, + ] + + +def test_aim_inspection_messages_preserves_safe_roles(): + """Safe roles pass through untouched — the coercion only fires for + roles the OpenAI chat schema flatten cannot represent standalone.""" + data = { + "messages": [ + {"role": "system", "content": "be helpful"}, + {"role": "user", "content": "hi"}, + {"role": "assistant", "content": "hello"}, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "system", "content": "be helpful"}, + {"role": "user", "content": "hi"}, + {"role": "assistant", "content": "hello"}, + ] diff --git a/tests/test_litellm/proxy/guardrails/test_content_utils.py b/tests/test_litellm/proxy/guardrails/test_content_utils.py index 099fca78a62..34d92505359 100644 --- a/tests/test_litellm/proxy/guardrails/test_content_utils.py +++ b/tests/test_litellm/proxy/guardrails/test_content_utils.py @@ -8,7 +8,6 @@ from litellm.proxy.guardrails._content_utils import ( walk_user_text, ) - # ── iter_message_text ──────────────────────────────────────────────────────────── @@ -101,6 +100,55 @@ def test_iter_message_text_empty_data(): assert list(iter_message_text({"input": ""})) == [] +def test_iter_message_text_responses_api_input_text_and_output_text_parts(): + """LIT-4294: Responses-API content parts use ``input_text`` (request) and + ``output_text`` (assistant); reading only ``type == "text"`` skipped every + ``/v1/responses`` body and every text guardrail was a no-op on that path.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "user text"}], + }, + { + "type": "message", + "role": "assistant", + "content": [{"type": "output_text", "text": "assistant text"}], + }, + ] + } + assert list(iter_message_text(data)) == ["user text", "assistant text"] + + +def test_iter_message_text_responses_api_tool_call_taxonomy(): + """LIT-4294: a Responses ``input`` list freely mixes message items, + ``function_call`` (no ``role``), and ``function_call_output`` items. The + old ``all(item has 'role')`` gate wrapped the whole list as one blob and + yielded nothing; every text fragment must be visited independently.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "hello"}], + }, + { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": "{}", + }, + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "sunny"}], + }, + ] + } + assert list(iter_message_text(data)) == ["hello", "sunny"] + + # ── walk_user_text ──────────────────────────────────────────────────────────── @@ -160,6 +208,89 @@ def test_walk_user_text_redacts_responses_api_list_input(): assert data["input"][1] == {"type": "image_url", "image_url": {"url": "..."}} +def test_walk_user_text_redacts_responses_input_text_and_output_text_parts(): + """LIT-4294: ``walk_user_text`` must recognise the Responses text-part + variants so masking guardrails (secret detection, PII) actually redact + ``/v1/responses`` bodies instead of no-op'ing on them.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "AKIAEXAMPLE"}], + }, + { + "type": "message", + "role": "assistant", + "content": [{"type": "output_text", "text": "AKIAEXAMPLE too"}], + }, + ] + } + visited = walk_user_text(data, lambda s: s.replace("AKIAEXAMPLE", "[REDACTED]")) + assert visited == 2 + assert data["input"][0]["content"][0] == { + "type": "input_text", + "text": "[REDACTED]", + } + assert data["input"][1]["content"][0] == { + "type": "output_text", + "text": "[REDACTED] too", + } + + +def test_walk_user_text_redacts_function_call_output_text(): + """LIT-4294: tool-call round-trips carry secrets in + ``function_call_output.output``; the redact walker must descend into it + while leaving ``function_call`` items (call_id, arguments) untouched.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "AKIAEXAMPLE user"}], + }, + { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": '{"AKIAEXAMPLE": 1}', + }, + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "AKIAEXAMPLE tool"}], + }, + ] + } + visited = walk_user_text(data, lambda s: s.replace("AKIAEXAMPLE", "[REDACTED]")) + assert visited == 2 + assert data["input"][0]["content"][0]["text"] == "[REDACTED] user" + assert data["input"][1] == { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": '{"AKIAEXAMPLE": 1}', + } + assert data["input"][2]["output"][0]["text"] == "[REDACTED] tool" + + +def test_walk_user_text_redacts_function_call_output_string_output(): + """LIT-4294: ``function_call_output.output`` is also a plain string in + OpenAI's Responses spec; the redact walker must handle both forms.""" + data = { + "input": [ + { + "type": "function_call_output", + "call_id": "c1", + "output": "AKIAEXAMPLE tool", + }, + ] + } + visited = walk_user_text(data, lambda s: s.replace("AKIAEXAMPLE", "[REDACTED]")) + assert visited == 1 + assert data["input"][0]["output"] == "[REDACTED] tool" + + def test_walk_user_text_redacts_mixed_list_input(): """Read and write helpers must agree on coverage — bare strings inside a mixed ``input`` list are inspected by both.""" @@ -206,17 +337,13 @@ def test_build_inspection_messages_joins_multimodal_text_parts(): } ] } - assert build_inspection_messages(data) == [ - {"role": "user", "content": "first part\nsecond part"} - ] + assert build_inspection_messages(data) == [{"role": "user", "content": "first part\nsecond part"}] def test_build_inspection_messages_lifts_responses_api_input(): """fniVO9-F: ``input`` must be visible to hooks that POST messages to a remote API.""" data = {"input": "responses-api content"} - assert build_inspection_messages(data) == [ - {"role": "user", "content": "responses-api content"} - ] + assert build_inspection_messages(data) == [{"role": "user", "content": "responses-api content"}] def test_build_inspection_messages_drops_messages_with_no_text(): @@ -233,6 +360,102 @@ def test_build_inspection_messages_drops_messages_with_no_text(): assert build_inspection_messages(data) == [{"role": "user", "content": "kept"}] +def test_build_inspection_messages_responses_api_tool_call_taxonomy(): + """LIT-4294: mixed Responses ``input`` (message + function_call + + function_call_output) must produce a non-empty inspection list. The + customer's writeup reproduced a 422 from AIM's ``/fw/v1/analyze`` + (``No messages in the request``) when this synthesised list came back + empty; every other guardrail silently scanned nothing on the same + input.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "hello"}], + }, + { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": "{}", + }, + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "sunny"}], + }, + ] + } + assert build_inspection_messages(data) == [ + {"role": "user", "content": "hello"}, + {"role": "tool", "content": "sunny"}, + ] + + +def test_build_inspection_messages_function_call_output_defaults_to_tool(): + """LIT-4294: a Responses ``function_call_output`` item is the semantic + equivalent of a chat-completions ``role: "tool"`` message, so the shared + helper synthesises ``role: "tool"`` when the item has no explicit role. + AIM's schema-safe coercion happens at the AIM call site, not here.""" + data = { + "input": [ + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "tool text"}], + }, + ] + } + assert build_inspection_messages(data) == [{"role": "tool", "content": "tool text"}] + + +def test_build_inspection_messages_function_call_output_preserves_explicit_role(): + """When ``function_call_output`` carries a caller-supplied ``role`` the + shared helper preserves it rather than synthesising ``tool``.""" + data = { + "input": [ + { + "type": "function_call_output", + "role": "assistant", + "call_id": "c1", + "output": [{"type": "input_text", "text": "tool text"}], + }, + ] + } + assert build_inspection_messages(data) == [{"role": "assistant", "content": "tool text"}] + + +def test_build_inspection_messages_bare_content_part_preserves_explicit_role(): + """A bare content-part dict with an explicit ``role`` keeps it. Only + absent roles get defaulted to ``user``.""" + data = { + "input": [ + {"type": "input_text", "text": "no role"}, + {"type": "output_text", "role": "assistant", "text": "with role"}, + ] + } + assert build_inspection_messages(data) == [ + {"role": "user", "content": "no role"}, + {"role": "assistant", "content": "with role"}, + ] + + +def test_build_inspection_messages_message_item_preserves_role(): + """Responses message items carry a role explicitly; the shared helper + passes it through untouched.""" + data = { + "input": [ + {"type": "message", "role": "system", "content": [{"type": "input_text", "text": "sys"}]}, + {"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "asst"}]}, + ] + } + assert build_inspection_messages(data) == [ + {"role": "system", "content": "sys"}, + {"role": "assistant", "content": "asst"}, + ] + + def test_build_inspection_messages_empty_data(): assert build_inspection_messages({}) == [] assert build_inspection_messages({"messages": []}) == [] From 1d870842125f52d901573cd0c2d6ba9c9399d39f Mon Sep 17 00:00:00 2001 From: Yassin Kortam Date: Thu, 9 Jul 2026 10:51:37 +0300 Subject: [PATCH 044/399] refactor(otel): move litellm error detail keys under the litellm.* namespace (#32591) The v2 OTel integration stamped litellm-specific error details as error.code, error.stack_trace, and error.llm_provider, squatting on the semconv-owned error.* namespace. They now live at litellm.provider.error.code, litellm.provider.error.stack_trace, and litellm.provider.error.llm_provider alongside the other vendor-extension keys. error.type and error.message stay on the semconv keys. --- litellm/integrations/otel/model/semconv.py | 19 ++++++------- .../otel/test_otel_v2_components.py | 28 +++++++++---------- .../otel/test_otel_v2_sources_of_truth.py | 2 -- 3 files changed, 21 insertions(+), 28 deletions(-) diff --git a/litellm/integrations/otel/model/semconv.py b/litellm/integrations/otel/model/semconv.py index 69d1e454655..aab80c7e5c4 100644 --- a/litellm/integrations/otel/model/semconv.py +++ b/litellm/integrations/otel/model/semconv.py @@ -147,24 +147,21 @@ class Error: """OTel-defined error attribute keys, from the semconv ``error.*`` registry. ``MESSAGE`` is marked *Deprecated* upstream in favor of domain-specific error message keys plus ``exception.message`` on the exception event, but - is still defined and stamped by litellm's v1 integration; keeping it here - for byte-for-byte parity.""" + litellm still stamps it.""" TYPE: Final = "error.type" MESSAGE: Final = "error.message" class LiteLLMError: - """LiteLLM-specific error attribute keys. Emitted under the ``error.*`` - namespace (not ``litellm.*``) for byte-for-byte compat with the v1 - integration in ``opentelemetry.py``; consumers reading these keys on v1 - spans read the same keys on v2 spans. OTel semconv does not define any of - these three, and per its extension rules a namespace may carry additional - vendor keys as long as they don't collide with defined names.""" + """Detail keys for the mapped provider exception of a failed LLM call. + OTel semconv does not define these, so they live under the ``litellm.*`` + vendor namespace rather than squatting on the semconv-owned ``error.*`` + namespace.""" - CODE: Final = "error.code" - STACK_TRACE: Final = "error.stack_trace" - LLM_PROVIDER: Final = "error.llm_provider" + CODE: Final = "litellm.provider.error.code" + STACK_TRACE: Final = "litellm.provider.error.stack_trace" + LLM_PROVIDER: Final = "litellm.provider.error.llm_provider" class ExceptionEvent: diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_components.py b/tests/test_litellm/integrations/otel/test_otel_v2_components.py index 298047ec18b..eb795a64b79 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_components.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_components.py @@ -604,8 +604,7 @@ def test_error_details_stamped_as_span_attributes_for_labels_ingest(): """OTel-defined keys and litellm-specific detail keys both ride span attributes so backends that flatten attrs into label indexes (Elastic APM ``labels.*``, Datadog span tags) render them. The exception event with the - full untruncated message stays alongside — both places, matching v1's - shape.""" + full untruncated message stays alongside.""" from litellm.integrations.otel.model.semconv import Error, ExceptionEvent, LiteLLMError from litellm.integrations.otel.emitter import SpanEmitter @@ -638,8 +637,8 @@ def test_error_details_stamped_as_span_attributes_for_labels_ingest(): # OTel-defined keys (from the ``error.*`` semconv registry). assert span.attributes[Error.TYPE] == "litellm.BadRequestError" assert span.attributes[Error.MESSAGE] == "400: violated moderation policy" - # LiteLLM-specific detail keys — vendor-namespaced under ``error.*`` - # for v1-parity, not defined by OTel semconv. + # LiteLLM-specific detail keys, under the ``litellm.provider.error.*`` + # vendor namespace, not defined by OTel semconv. assert span.attributes[LiteLLMError.CODE] == "400" assert span.attributes[LiteLLMError.STACK_TRACE] == "File proxy_server.py line 8570 ..." assert span.attributes[LiteLLMError.LLM_PROVIDER] == "openai" @@ -666,19 +665,18 @@ def test_error_details_omitted_when_span_error_carries_only_message(): assert LiteLLMError.LLM_PROVIDER not in span.attributes -def test_v2_error_attribute_keys_match_v1_error_attributes_byte_for_byte(): - """v1 (``opentelemetry.py``) and v2 (``otel/`` package) stamp identical - span-attribute keys so consumers reading ``labels.error_message`` don't - care which integration produced the span. Renaming either side is a - breaking change for downstream dashboards; this test locks the vocabulary.""" - from litellm.integrations._types.open_inference import ErrorAttributes +def test_error_attribute_keys_are_pinned(): + """``error.type`` and ``error.message`` come from the semconv ``error.*`` + registry; the litellm-specific detail keys are vendor keys under + ``litellm.provider.error.*``. Pins the exact strings so the emitted + vocabulary can't drift silently.""" from litellm.integrations.otel.model.semconv import Error, LiteLLMError - assert Error.TYPE == ErrorAttributes.ERROR_TYPE - assert Error.MESSAGE == ErrorAttributes.ERROR_MESSAGE - assert LiteLLMError.CODE == ErrorAttributes.ERROR_CODE - assert LiteLLMError.STACK_TRACE == ErrorAttributes.ERROR_STACK_TRACE - assert LiteLLMError.LLM_PROVIDER == ErrorAttributes.ERROR_LLM_PROVIDER + assert Error.TYPE == "error.type" + assert Error.MESSAGE == "error.message" + assert LiteLLMError.CODE == "litellm.provider.error.code" + assert LiteLLMError.STACK_TRACE == "litellm.provider.error.stack_trace" + assert LiteLLMError.LLM_PROVIDER == "litellm.provider.error.llm_provider" def test_error_message_falls_back_to_error_type_when_message_absent(): diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py b/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py index 89aa73a6066..71be28ea485 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py @@ -147,8 +147,6 @@ def test_attribute_keys_are_unique_across_namespaces(): from litellm.integrations.otel import MCP, Client, JsonRpc, LiteLLMError, Network # prefixes are allowed to be substrings; exact keys must not collide. - # ``LiteLLMError`` shares the ``error.*`` prefix with ``Error`` by design - # (v1-parity); the assert below is the guarantee they never overlap. exact = set() for cls in (GenAI, Error, LiteLLMError, Server, HTTP, DB, MCP, JsonRpc, Network, Client): for key in _all_constants(cls): From cda99a08c8eda814e573404d09e899a1f81b3646 Mon Sep 17 00:00:00 2001 From: Yassin Kortam Date: Thu, 9 Jul 2026 11:13:13 +0300 Subject: [PATCH 045/399] fix(proxy): surface OAuth error params in SSO callback (#32433) When an IdP denies SSO access it redirects back to /sso/callback with error and error_description query params and no code param. The callback previously fell through to the provider token exchange, which failed with a generic "'code' parameter was not found in callback request" 400 that hides the real denial reason. Raise a 401 that surfaces the IdP's error and description instead. Ported from #26640 with conflicts resolved against current staging --- litellm/proxy/management_endpoints/ui_sso.py | 12 ++++ .../proxy/management_endpoints/test_ui_sso.py | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index 43fdd3ed05a..065464aa565 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -1746,6 +1746,18 @@ async def auth_callback(request: Request, state: Optional[str] = None): """Verify login""" verbose_proxy_logger.info(f"Starting SSO callback with state: {state}") + oauth_error = request.query_params.get("error") + if oauth_error: + oauth_error_description = request.query_params.get("error_description") + verbose_proxy_logger.warning( + f"SSO callback received OAuth error: {oauth_error}, description: {oauth_error_description}" + ) + raise HTTPException( + status_code=401, + detail=f"OAuth error: {oauth_error}" + + (f", error_description: {oauth_error_description}" if oauth_error_description else ""), + ) + # Check if this is a CLI login (state starts with our CLI prefix) from litellm.constants import LITELLM_CLI_SESSION_TOKEN_PREFIX from litellm.proxy._types import LiteLLM_JWTAuth diff --git a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py index 642f20906a0..045e15f8b8b 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -2783,6 +2783,7 @@ class TestCLIKeyRegenerationFlow: # Mock request mock_request = MagicMock(spec=Request) + mock_request.query_params = {"code": "some-auth-code"} cli_state = f"{LITELLM_CLI_SESSION_TOKEN_PREFIX}:cli-new-session-key-456" @@ -2824,6 +2825,7 @@ class TestCLIKeyRegenerationFlow: from litellm.proxy.management_endpoints.ui_sso import auth_callback mock_request = MagicMock(spec=Request) + mock_request.query_params = {"code": "some-auth-code"} cli_state = ( f"{LITELLM_CLI_SESSION_TOKEN_PREFIX}:cli-new-session-key-456:WXYZ-2345" ) @@ -7254,3 +7256,61 @@ async def test_cli_poll_key_tolerates_missing_user_row(): assert result["status"] == "ready" assert result["key"] == mock_jwt_token assert result["user_id"] == "just-created-user" + + +def _make_sso_callback_request(query_params: dict) -> MagicMock: + mock_request = MagicMock(spec=Request) + mock_request.query_params = query_params + return mock_request + + +@pytest.mark.asyncio +async def test_auth_callback_surfaces_oauth_error_with_description(): + """ + Regression: when the IdP denies access it redirects back with + ?error=...&error_description=... and no `code`. The callback must surface + that reason as a 401 instead of failing later on the missing `code` param. + """ + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = _make_sso_callback_request( + {"error": "access_denied", "error_description": "User is not assigned to the client application"} + ) + + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state=None) + + assert exc_info.value.status_code == 401 + assert "access_denied" in str(exc_info.value.detail) + assert "User is not assigned to the client application" in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_auth_callback_surfaces_oauth_error_without_description(): + """error_description is optional in the OAuth error response; the 401 detail must not render 'None'.""" + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = _make_sso_callback_request({"error": "access_denied"}) + + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state=None) + + assert exc_info.value.status_code == 401 + assert "access_denied" in str(exc_info.value.detail) + assert "None" not in str(exc_info.value.detail) + assert "error_description" not in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_auth_callback_without_oauth_error_proceeds_to_normal_flow(): + """Without an `error` query param the guard must not fire; the callback proceeds into the normal flow.""" + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = _make_sso_callback_request({"code": "some-auth-code"}) + + with patch("litellm.proxy.proxy_server.prisma_client", None): + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state=None) + + assert exc_info.value.status_code == 500 + assert "DB not connected" in str(exc_info.value.detail) From 60729f733ec7dd1d2a37826c3bb776e27daa6d11 Mon Sep 17 00:00:00 2001 From: Yassin Kortam Date: Thu, 9 Jul 2026 11:14:22 +0300 Subject: [PATCH 046/399] test(benchmarks): run shared logging executor inline to make CodSpeed measurements deterministic (#32435) --- tests/benchmarks/conftest.py | 36 +++++++++++++++++++++++++++++ tests/benchmarks/test_benchmarks.py | 21 +++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/benchmarks/conftest.py diff --git a/tests/benchmarks/conftest.py b/tests/benchmarks/conftest.py new file mode 100644 index 00000000000..c9b31cfb7d7 --- /dev/null +++ b/tests/benchmarks/conftest.py @@ -0,0 +1,36 @@ +"""Shared setup keeping CodSpeed measurements hermetic. + +CodSpeed's callgrind instrumentation counts instructions from every thread while +a measurement window is open, and valgrind serializes all threads onto one +virtual CPU. Work deferred to litellm's shared logging executor would therefore +be attributed to whichever benchmark the valgrind scheduler resumes it under, +flipping results between runs. Running the executor inline keeps each +benchmark's cost self-contained and deterministic. +""" + +from collections.abc import Callable, Iterator +from concurrent.futures import Future +from typing import ParamSpec, TypeVar + +import pytest + +from litellm.litellm_core_utils.thread_pool_executor import executor + +P = ParamSpec("P") +R = TypeVar("R") + + +def _submit_inline(fn: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> Future[R]: + future: Future[R] = Future() + try: + future.set_result(fn(*args, **kwargs)) + except BaseException as exc: + future.set_exception(exc) + return future + + +@pytest.fixture(autouse=True, scope="session") +def inline_logging_executor() -> Iterator[None]: + executor.submit = _submit_inline + yield + del executor.submit diff --git a/tests/benchmarks/test_benchmarks.py b/tests/benchmarks/test_benchmarks.py index 123dad93e11..59b3e0b6d5c 100644 --- a/tests/benchmarks/test_benchmarks.py +++ b/tests/benchmarks/test_benchmarks.py @@ -6,10 +6,13 @@ in the litellm hot path: token counting, model info lookup, provider resolution, and cost calculation. """ +import threading + import pytest import litellm from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider +from litellm.litellm_core_utils.thread_pool_executor import executor from litellm.litellm_core_utils.token_counter import token_counter @@ -205,3 +208,21 @@ def test_get_model_cost_key_exact_match(): def test_get_model_cost_key_case_insensitive(): """Benchmark model cost key lookup with case-insensitive fallback.""" litellm.utils._get_model_cost_key("GPT-4o") + + +# --------------------------------------------------------------------------- +# Measurement hermeticity guard +# --------------------------------------------------------------------------- + + +@pytest.mark.benchmark +def test_logging_executor_runs_inline(): + """Guard that the shared logging executor runs submissions inline. + + Deferred submissions execute on worker threads, and callgrind attributes + their instructions to whichever benchmark's measurement window is open when + the valgrind scheduler resumes them, making results nondeterministic. + """ + future = executor.submit(threading.get_ident) + assert future.done() + assert future.result() == threading.get_ident() From c36525f9bef7549aa1810b50834ab131a7bcd531 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 09:10:32 -0700 Subject: [PATCH 047/399] bump: litellm-enterprise 0.1.48 -> 0.1.49 --- enterprise/pyproject.toml | 4 ++-- pyproject.toml | 2 +- uv.lock | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/enterprise/pyproject.toml b/enterprise/pyproject.toml index b3864ce7878..85ccbef752f 100644 --- a/enterprise/pyproject.toml +++ b/enterprise/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm-enterprise" -version = "0.1.48" +version = "0.1.49" description = "Package for LiteLLM Enterprise features" readme = "README.md" requires-python = ">=3.9" @@ -26,7 +26,7 @@ required-version = ">=0.10.9" module-root = "" [tool.commitizen] -version = "0.1.48" +version = "0.1.49" version_files = [ "pyproject.toml:^version", "../pyproject.toml:litellm-enterprise==", diff --git a/pyproject.toml b/pyproject.toml index 3f5458c5494..99425ef3aef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ proxy = [ "azure-storage-blob>=12.28.0,<13.0", "mcp>=1.26.0,<2.0", "litellm-proxy-extras==0.4.75", - "litellm-enterprise==0.1.48", + "litellm-enterprise==0.1.49", "RestrictedPython>=8.1,<9.0", "rich>=13.9.4,<14.0", "polars>=1.38.1,<2.0", diff --git a/uv.lock b/uv.lock index e36da722261..a226ef172c7 100644 --- a/uv.lock +++ b/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-07-04T17:13:14.93495Z" +exclude-newer = "2026-07-06T16:10:51.214184Z" exclude-newer-span = "P3D" [manifest] @@ -3639,7 +3639,7 @@ proxy-dev = [ [[package]] name = "litellm-enterprise" -version = "0.1.48" +version = "0.1.49" source = { editable = "enterprise" } [[package]] From b3a44bd1b2d46f2fd5f53a49107a6c1105901169 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:03:28 +0000 Subject: [PATCH 048/399] fix(deps): constrain soupsieve>=2.8.4 to patch two high-severity CVEs --- pyproject.toml | 1 + uv.lock | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3f5458c5494..d0ea2b9da4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -259,6 +259,7 @@ constraint-dependencies = [ "tornado>=6.5.6", "aiohttp>=3.14.1,<4.0", "packaging>=24.0", + "soupsieve>=2.8.4", ] override-dependencies = [ # a2a-sdk 1.x requires packaging>=24.0; lunary 1.4.x still caps at <24.0. diff --git a/uv.lock b/uv.lock index e36da722261..313fd682ccf 100644 --- a/uv.lock +++ b/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-07-04T17:13:14.93495Z" +exclude-newer = "2026-07-06T17:03:18.138046444Z" exclude-newer-span = "P3D" [manifest] @@ -21,6 +21,7 @@ members = [ constraints = [ { name = "aiohttp", specifier = ">=3.14.1,<4.0" }, { name = "packaging", specifier = ">=24.0" }, + { name = "soupsieve", specifier = ">=2.8.4" }, { name = "tornado", specifier = ">=6.5.6" }, ] overrides = [{ name = "packaging", specifier = ">=24.0" }] @@ -7076,11 +7077,11 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8.3" +version = "2.8.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, ] [[package]] From ceeb90abdba02d502ee6391014a84954b406b852 Mon Sep 17 00:00:00 2001 From: Tin Date: Tue, 7 Jul 2026 20:17:35 -0700 Subject: [PATCH 049/399] feat(ui): expose true_passthrough and oauth_delegate auth types with a no-auth warning Adds the two client-forwarded token modes to the MCP server create and edit form auth dropdowns, and shows a warning when true_passthrough is selected: the gateway performs no admission auth for that server, so callers reach the upstream without a LiteLLM key and per-key/per-team rate limits and spend tracking do not apply. The warning is a shared component so the two forms cannot drift on the copy. --- .../mcp_tools/TruePassthroughWarning.tsx | 21 ++++++++++ .../mcp_tools/create_mcp_server.test.tsx | 25 ++++++++++++ .../mcp_tools/create_mcp_server.tsx | 7 ++++ .../mcp_tools/mcp_server_edit.test.tsx | 39 +++++++++++++++++++ .../components/mcp_tools/mcp_server_edit.tsx | 32 +++++++++------ .../src/components/mcp_tools/types.tsx | 2 + 6 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/mcp_tools/TruePassthroughWarning.tsx diff --git a/ui/litellm-dashboard/src/components/mcp_tools/TruePassthroughWarning.tsx b/ui/litellm-dashboard/src/components/mcp_tools/TruePassthroughWarning.tsx new file mode 100644 index 00000000000..b52d3f4c672 --- /dev/null +++ b/ui/litellm-dashboard/src/components/mcp_tools/TruePassthroughWarning.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Alert } from "antd"; +import { AUTH_TYPE } from "./types"; + +/** + * Warning shown in the create/edit MCP server forms when auth_type + * true_passthrough is selected: the gateway performs no admission auth for + * that server, so callers reach the upstream without a LiteLLM identity. + */ +export default function TruePassthroughWarning({ authType }: { authType?: string | null }) { + if (authType !== AUTH_TYPE.TRUE_PASSTHROUGH) return null; + return ( + + ); +} diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index 36af2f8d9fc..27e6d6a8e3d 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -164,6 +164,31 @@ describe("CreateMCPServer", () => { }); }); + it("should warn that LiteLLM auth is disabled when True Passthrough is selected", async () => { + await selectHttpTransport(); + + await selectAntOption("Authentication", "True Passthrough (no LiteLLM auth)"); + + await waitFor(() => { + expect( + screen.getByText("True Passthrough disables LiteLLM authentication for this server"), + ).toBeInTheDocument(); + }); + }); + + it("should not show the True Passthrough warning when OAuth Delegate is selected", async () => { + await selectHttpTransport(); + + await selectAntOption("Authentication", "OAuth Delegate (client-supplied upstream token)"); + + await waitFor(() => { + expect(screen.getAllByText("OAuth Delegate (client-supplied upstream token)").length).toBeGreaterThan(0); + }); + expect( + screen.queryByText("True Passthrough disables LiteLLM authentication for this server"), + ).not.toBeInTheDocument(); + }); + it("should not require auth value when creating a server with API Key auth type", async () => { await selectHttpTransport(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 10668468c15..1362835f475 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -16,6 +16,7 @@ import { MCP_OAUTH2_FLOW_INTERACTIVE, } from "./types"; import OAuthFormFields from "./OAuthFormFields"; +import TruePassthroughWarning from "./TruePassthroughWarning"; import TokenExchangeFormFields from "./TokenExchangeFormFields"; import MCPServerCostConfig from "./mcp_server_cost_config"; import MCPConnectionStatus from "./mcp_connection_status"; @@ -970,9 +971,15 @@ const CreateMCPServer: React.FC = ({ OAuth OAuth Token Exchange (OBO) AWS SigV4 (Bedrock AgentCore MCPs) + True Passthrough (no LiteLLM auth) + + OAuth Delegate (client-supplied upstream token) + + + {shouldShowAuthValueField && ( { }); }); +describe("MCPServerEdit (true passthrough warning)", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + const renderWithAuthType = (authType: string) => + render( + , + ); + + it("warns that LiteLLM auth is disabled for a true_passthrough server", async () => { + renderWithAuthType("true_passthrough"); + + await waitFor(() => { + expect(screen.getByText("True Passthrough disables LiteLLM authentication for this server")).toBeInTheDocument(); + }); + }); + + it("does not warn for an oauth_delegate server", async () => { + renderWithAuthType("oauth_delegate"); + + await waitFor(() => { + expect(screen.getAllByRole("button", { name: "Save Changes" }).length).toBeGreaterThan(0); + }); + expect( + screen.queryByText("True Passthrough disables LiteLLM authentication for this server"), + ).not.toBeInTheDocument(); + }); +}); + describe("MCPServerEdit (auth type switch)", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 70632b459fc..2c4f674c14b 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -18,6 +18,7 @@ import { getToken, isTokenValid, setToken } from "@/utils/mcpTokenStore"; import { buildMcpPassthroughAuthHeader } from "@/utils/mcpHeaderUtils"; import MCPServerCostConfig from "./mcp_server_cost_config"; import MCPPermissionManagement from "./MCPPermissionManagement"; +import TruePassthroughWarning from "./TruePassthroughWarning"; import MCPToolConfiguration from "./mcp_tool_configuration"; import StdioConfiguration from "./StdioConfiguration"; import TokenExchangeFormFields from "./TokenExchangeFormFields"; @@ -874,18 +875,25 @@ const MCPServerEdit: React.FC = ({ {/* Authentication - for HTTP, SSE, and OpenAPI */} {!isStdioTransport && ( - - - + <> + + + + + )} {isStdioTransport && ( diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx index 9469d7bd89e..70cc8129bf1 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx @@ -41,6 +41,8 @@ export const AUTH_TYPE = { OAUTH2: "oauth2", OAUTH2_TOKEN_EXCHANGE: "oauth2_token_exchange", AWS_SIGV4: "aws_sigv4", + TRUE_PASSTHROUGH: "true_passthrough", + OAUTH_DELEGATE: "oauth_delegate", }; export const OAUTH_FLOW = { From 22ab518071716688f9b0f70003fb30780ca6ff42 Mon Sep 17 00:00:00 2001 From: Tin Date: Tue, 7 Jul 2026 21:29:43 -0700 Subject: [PATCH 050/399] feat(ui): browser-only Authorize & Fetch for the client-forwarded token modes true_passthrough and oauth_delegate persist no upstream credentials, so the create/edit forms had no way to preview tools or configure the tool allowlist: tools/list went upstream unauthenticated and came back 401. This reuses the existing OAuth authorize machinery in browser-only mode for those two auth types: the admin authorizes against the upstream (DCR/PKCE, with optional client credentials for IdPs without dynamic registration), the token lands in sessionStorage exactly like the legacy PKCE-passthrough path, and the tools preview forwards it via the per-server x-mcp-{alias}-authorization header, which the passthrough resolver arm already accepts. Nothing is written to the server row or the per-user credential store; the create payload keeps excluding credentials for these auth types via AUTH_TYPES_REQUIRING_CREDENTIALS. The tools preview endpoint now also extracts the Authorization header for the two new auth types so the browser-held token reaches the passthrough arm during create-time previews. --- .../mcp_server/rest_endpoints.py | 6 +- .../mcp_server/test_rest_endpoints.py | 53 +++++++++++++ .../mcp_tools/PassthroughAuthorizeSection.tsx | 74 +++++++++++++++++++ .../mcp_tools/create_mcp_server.test.tsx | 26 +++++++ .../mcp_tools/create_mcp_server.tsx | 11 +++ .../mcp_tools/mcp_server_edit.test.tsx | 49 ++++++++++++ .../components/mcp_tools/mcp_server_edit.tsx | 66 +++++++++++++---- .../src/hooks/useTestMCPConnection.tsx | 4 +- 8 files changed, 272 insertions(+), 17 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index b917530dd52..21682b4dd3e 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -1322,7 +1322,11 @@ if MCP_AVAILABLE: mcp_auth_header = credentials.get("auth_value") oauth2_headers: Optional[Dict[str, str]] = None - if new_mcp_server_request.auth_type == MCPAuth.oauth2: + if new_mcp_server_request.auth_type in { + MCPAuth.oauth2, + MCPAuth.true_passthrough, + MCPAuth.oauth_delegate, + }: oauth2_headers = MCPRequestHandler._get_oauth2_headers_from_headers(headers) async def _list_tools_operation(client): diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py index 3d9afd8f250..090b4711dc9 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py @@ -593,6 +593,59 @@ class TestTestToolsList: assert captured["oauth2_headers"] == oauth_headers assert oauth_call_counter["count"] == 1 + @pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_extracts_oauth2_headers_for_client_forwarded_modes(self, monkeypatch, auth_type): + """The browser-only authorize flow sends the upstream token as Authorization; the preview + must thread it through for the client-forwarded token modes so the passthrough arm can + forward it, instead of probing the upstream unauthenticated.""" + + captured: dict = {} + + async def fake_execute( + request, + operation, + mcp_auth_header=None, + oauth2_headers=None, + raw_headers=None, + ): + captured["mcp_auth_header"] = mcp_auth_header + captured["oauth2_headers"] = oauth2_headers + return { + "tools": [], + "error": None, + "message": "Successfully retrieved tools", + } + + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) + + oauth_headers = {"Authorization": "Bearer upstream-token"} + + monkeypatch.setattr( + auth_mcp.MCPRequestHandler, + "_get_oauth2_headers_from_headers", + staticmethod(lambda headers: oauth_headers), + raising=False, + ) + + request = _build_request({"authorization": "Bearer upstream-token"}) + payload = NewMCPServerRequest( + server_name="example", + url="https://example.com", + auth_type=auth_type, + ) + + from litellm.proxy._types import LitellmUserRoles + + result = await rest_endpoints.test_tools_list( + request, + payload, + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert result["message"] == "Successfully retrieved tools" + assert captured["mcp_auth_header"] is None + assert captured["oauth2_headers"] == oauth_headers + class TestListToolsRestAPI: pytestmark = pytest.mark.asyncio diff --git a/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx b/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx new file mode 100644 index 00000000000..453e3024000 --- /dev/null +++ b/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import { Button, Form, Input } from "antd"; +import { AUTH_TYPE } from "./types"; + +interface PassthroughOAuthFlow { + startOAuthFlow: () => void | Promise; + status: string; + error: string | null; + tokenResponse: { access_token?: string; expires_in?: number } | null; +} + +/** + * Browser-only Authorize & Fetch for the client-forwarded token modes + * (true_passthrough / oauth_delegate). LiteLLM never stores upstream + * credentials for these modes, so the token obtained here lives in this + * browser session only: it is forwarded per-server for the tools preview and + * allowlist configuration, and is never written to the server row or the + * per-user credential store. The optional client credentials cover IdPs + * without dynamic client registration (e.g. a pre-registered Slack app) and + * ride the temporary authorize session only. + */ +export default function PassthroughAuthorizeSection({ + authType, + oauthFlow, +}: { + authType?: string | null; + oauthFlow: PassthroughOAuthFlow; +}) { + if (authType !== AUTH_TYPE.TRUE_PASSTHROUGH && authType !== AUTH_TYPE.OAUTH_DELEGATE) return null; + return ( +
+

+ Callers bring their own upstream token for this auth type, so LiteLLM stores no upstream credentials. To preview + tools and configure the tool allowlist, authorize against the upstream here: the token stays in this browser + session only and is never saved to LiteLLM. +

+ OAuth Client ID (optional, not saved)} + name={["credentials", "client_id"]} + extra="Only needed when the upstream does not support dynamic client registration (e.g. a pre-registered Slack app). Used for this browser authorization only." + > + + + OAuth Client Secret (optional, not saved)} + name={["credentials", "client_secret"]} + > + + + + {oauthFlow.error &&

{oauthFlow.error}

} + {oauthFlow.status === "success" && oauthFlow.tokenResponse?.access_token && ( +

+ Token held for this browser session. Tools can now be previewed and configured; nothing was saved to LiteLLM. +

+ )} +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index 27e6d6a8e3d..ca94aae20e9 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -189,6 +189,32 @@ describe("CreateMCPServer", () => { ).not.toBeInTheDocument(); }); + it.each([["True Passthrough (no LiteLLM auth)"], ["OAuth Delegate (client-supplied upstream token)"]])( + "should show the browser-only authorize section when %s is selected", + async (optionLabel) => { + await selectHttpTransport(); + + await selectAntOption("Authentication", optionLabel); + + await waitFor(() => { + expect(screen.getByRole("button", { name: "Authorize & Fetch Tools (browser-only)" })).toBeInTheDocument(); + }); + expect(screen.getByText("OAuth Client ID (optional, not saved)")).toBeInTheDocument(); + expect(screen.getByText("OAuth Client Secret (optional, not saved)")).toBeInTheDocument(); + }, + ); + + it("should not show the browser-only authorize section for API Key auth", async () => { + await selectHttpTransport(); + + await selectAntOption("Authentication", "API Key"); + + await waitFor(() => { + expect(screen.getByText("Authentication Value")).toBeInTheDocument(); + }); + expect(screen.queryByRole("button", { name: "Authorize & Fetch Tools (browser-only)" })).not.toBeInTheDocument(); + }); + it("should not require auth value when creating a server with API Key auth type", async () => { await selectHttpTransport(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 1362835f475..b123e3397a1 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -17,6 +17,7 @@ import { } from "./types"; import OAuthFormFields from "./OAuthFormFields"; import TruePassthroughWarning from "./TruePassthroughWarning"; +import PassthroughAuthorizeSection from "./PassthroughAuthorizeSection"; import TokenExchangeFormFields from "./TokenExchangeFormFields"; import MCPServerCostConfig from "./mcp_server_cost_config"; import MCPConnectionStatus from "./mcp_connection_status"; @@ -980,6 +981,16 @@ const CreateMCPServer: React.FC = ({ + + {shouldShowAuthValueField && ( { expect(mockGetToken).toHaveBeenCalledWith("oauth_server_1", "user-1"); }); + it("forwards the sessionStorage token as the x-mcp header for an oauth_delegate server", async () => { + mockIsTokenValid.mockReturnValue(true); + mockGetToken.mockReturnValue({ access_token: "browser-token" }); + + render( + , + ); + + await waitFor(() => { + expect(networking.listMCPTools).toHaveBeenCalledWith( + "access-token", + "oauth_server_1", + { "x-mcp-oauth_server-authorization": "Bearer browser-token" }, + true, + ); + }); + expect(mockGetToken).toHaveBeenCalledWith("oauth_server_1", "user-1"); + }); + + it("prompts for the browser-only authorize when a true_passthrough server has no token", async () => { + mockIsTokenValid.mockReturnValue(false); + + render( + , + ); + + await waitFor(() => { + expect(screen.getByTestId("mcp-tool-config").getAttribute("data-external-error")).toContain( + "Authorize with the upstream (browser-only", + ); + }); + expect(networking.listMCPTools).not.toHaveBeenCalled(); + expect(screen.getByRole("button", { name: "Authorize & Fetch Tools (browser-only)" })).toBeInTheDocument(); + }); + it("uses the staged OAuth token to load passthrough tools after authorize", async () => { const passthroughServer = { ...interactiveOAuthServer, delegate_auth_to_upstream: true }; mockIsTokenValid.mockReturnValue(false); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 2c4f674c14b..ee67ead8121 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -19,6 +19,7 @@ import { buildMcpPassthroughAuthHeader } from "@/utils/mcpHeaderUtils"; import MCPServerCostConfig from "./mcp_server_cost_config"; import MCPPermissionManagement from "./MCPPermissionManagement"; import TruePassthroughWarning from "./TruePassthroughWarning"; +import PassthroughAuthorizeSection from "./PassthroughAuthorizeSection"; import MCPToolConfiguration from "./mcp_tool_configuration"; import StdioConfiguration from "./StdioConfiguration"; import TokenExchangeFormFields from "./TokenExchangeFormFields"; @@ -172,20 +173,40 @@ const MCPServerEdit: React.FC = ({ }; }, onTokenReceived: (token) => { - if (token?.access_token) { - const credentials = { - access_token: token.access_token, - ...(token.refresh_token && { refresh_token: token.refresh_token }), - ...(token.expires_in && { expires_in: token.expires_in }), - ...(token.scope && { scope: token.scope }), - }; - - form.setFieldsValue({ credentials }); - - NotificationsManager.success( - "OAuth authorization successful! Please click 'Update MCP Server' to save the credentials.", - ); + if (!token?.access_token) { + return; } + + const effectiveAuthType = form.getFieldValue("auth_type") ?? mcpServer.auth_type; + if (effectiveAuthType === AUTH_TYPE.TRUE_PASSTHROUGH || effectiveAuthType === AUTH_TYPE.OAUTH_DELEGATE) { + setToken( + mcpServer.server_id, + { + access_token: token.access_token, + expires_in: token.expires_in, + refresh_token: token.refresh_token, + token_type: token.token_type, + }, + userID, + ); + NotificationsManager.success( + "Token held for this browser session. Tools can now be loaded and configured; nothing was saved to LiteLLM.", + ); + return; + } + + const credentials = { + access_token: token.access_token, + ...(token.refresh_token && { refresh_token: token.refresh_token }), + ...(token.expires_in && { expires_in: token.expires_in }), + ...(token.scope && { scope: token.scope }), + }; + + form.setFieldsValue({ credentials }); + + NotificationsManager.success( + "OAuth authorization successful! Please click 'Update MCP Server' to save the credentials.", + ); }, onBeforeRedirect: persistEditUiState, flowSource: "edit", @@ -369,7 +390,9 @@ const MCPServerEdit: React.FC = ({ oauth2_flow: mcpServer.oauth2_flow, delegate_auth_to_upstream: mcpServer.delegate_auth_to_upstream, }) === "passthrough"; - if (isPassthrough) { + const isBrowserHeldTokenMode = + mcpServer.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || mcpServer.auth_type === AUTH_TYPE.OAUTH_DELEGATE; + if (isPassthrough || isBrowserHeldTokenMode) { const token = oauthTokenResponse?.access_token ?? (isTokenValid(mcpServer.server_id, userID) @@ -377,7 +400,11 @@ const MCPServerEdit: React.FC = ({ : null); if (!token) { setTools([]); - setToolsError("Authenticate with this server in the Tools tab to load and configure its tools."); + setToolsError( + isBrowserHeldTokenMode + ? "Authorize with the upstream (browser-only, in the Authentication section) to load and configure this server's tools." + : "Authenticate with this server in the Tools tab to load and configure its tools.", + ); return; } customHeaders = buildMcpPassthroughAuthHeader(mcpServer.alias, token); @@ -893,6 +920,15 @@ const MCPServerEdit: React.FC = ({ + )} diff --git a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx index 055e15350ca..3208b6b02b2 100644 --- a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx +++ b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx @@ -56,7 +56,9 @@ export const useTestMCPConnection = ({ // Check if we have the minimum required fields to fetch tools const isM2MOAuth = formValues.auth_type === AUTH_TYPE.OAUTH2 && formValues.oauth_flow_type === OAUTH_FLOW.M2M; - const requiresOAuthToken = formValues.auth_type === AUTH_TYPE.OAUTH2 && !isM2MOAuth; + const isBrowserHeldTokenMode = + formValues.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || formValues.auth_type === AUTH_TYPE.OAUTH_DELEGATE; + const requiresOAuthToken = (formValues.auth_type === AUTH_TYPE.OAUTH2 && !isM2MOAuth) || isBrowserHeldTokenMode; const isOpenAPITransport = formValues.transport === TRANSPORT.OPENAPI; const hasEndpoint = isOpenAPITransport ? !!formValues.spec_path : !!formValues.url; From 367aa904de68c6945c6006261b6a1ff6017b750f Mon Sep 17 00:00:00 2001 From: Tin Date: Tue, 7 Jul 2026 21:52:56 -0700 Subject: [PATCH 051/399] fix(ui): wire the tool playground and gateway authorize flow for the client-forwarded token modes The server detail page's Tool Testing Playground gated its browser-held token handling on the legacy PKCE-passthrough shape, so a true_passthrough or oauth_delegate server listed tools unauthenticated and surfaced 'Failed to fetch MCP tools' with no way to authorize. The playground now treats both modes as browser-held-token servers: it reads the sessionStorage token established by the create/edit browser-only Authorize, forwards it via the x-mcp-{alias}-authorization header, evicts it on a 401, and shows its own Authorize gate when the token is absent. That gate's flow uses the gateway's relayed authorize/register/token endpoints with the real server id, which previously 400ed for anything but oauth2. Those endpoints now also accept the client-forwarded token modes (the minted token is upstream-audienced and browser-held; DCR persistence stays off on this path), and registry builds run the same RFC 9728/8414 endpoint discovery for these modes that oauth2 rows get, since their rows never store an authorization_url. --- .../mcp_server/discoverable_endpoints.py | 14 +++-- .../mcp_server/mcp_server_manager.py | 5 +- .../mcp_server/test_discoverable_endpoints.py | 55 +++++++++++++++++++ .../mcp_server/test_mcp_server_manager.py | 33 +++++++++++ .../components/mcp_tools/mcp_tools.test.tsx | 31 +++++++++++ .../src/components/mcp_tools/mcp_tools.tsx | 23 +++++--- 6 files changed, 147 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index c87e900aa2a..7606deac241 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -465,8 +465,15 @@ async def _store_per_user_token_server_side( def _raise_if_not_oauth2(mcp_server: MCPServer) -> None: - """Reject a non-oauth2 server from the gateway's OAuth authorize/token/register flow.""" - if mcp_server.auth_type == MCPAuth.oauth2: + """Reject a server without upstream OAuth from the gateway's authorize/token/register flow. + + The client-forwarded token modes (``true_passthrough`` / ``oauth_delegate``) are allowed + through: the caller owns the upstream token, and this relayed flow is how a browser obtains + one against the upstream IdP (the admin UI's browser-only Authorize uses it). The minted + token is upstream-audienced and held by the caller; the gateway persists nothing for these + modes (DCR persistence is opt-in and never enabled on this path). + """ + if mcp_server.auth_type in (MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate): return raise HTTPException( status_code=400, @@ -515,8 +522,7 @@ async def authorize_with_server( response_type: Optional[str] = None, scope: Optional[str] = None, ): - if mcp_server.auth_type != "oauth2": - raise HTTPException(status_code=400, detail="MCP server is not OAuth2") + _raise_if_not_oauth2(mcp_server) if mcp_server.authorization_url is None: raise HTTPException(status_code=400, detail="MCP server authorization url is not set") diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index c4ad673b88f..c8681b94f5e 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -1384,8 +1384,9 @@ class MCPServerManager: auth_type = cast(MCPAuthType, mcp_server.auth_type) server_url = mcp_server.url + upstream_oauth_auth_types = (MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate) needs_discovery = bool(server_url) and ( - (auth_type == MCPAuth.oauth2 and not mcp_server.authorization_url) + (auth_type in upstream_oauth_auth_types and not mcp_server.authorization_url) or self._obo_needs_endpoint_discovery( auth_type, mcp_server.token_exchange_endpoint @@ -1396,7 +1397,7 @@ class MCPServerManager: mcp_oauth_metadata = ( await self._descovery_metadata( server_url=server_url, # type: ignore[arg-type] - allow_origin_fallback=auth_type == MCPAuth.oauth2, + allow_origin_fallback=auth_type in upstream_oauth_auth_types, ) if needs_discovery else None diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 19d030f17c4..926c3d5a1f4 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -119,6 +119,61 @@ async def test_authorize_endpoint_includes_response_type(): assert "scope=read+write" in response.headers["location"] +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type_value", ["true_passthrough", "oauth_delegate"]) +async def test_authorize_endpoint_allows_client_forwarded_modes(auth_type_value): + """The browser-only Authorize relays the gateway authorize flow for the client-forwarded + token modes; the oauth2-only gate must let them through and redirect to the upstream IdP.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + + server = MCPServer( + server_id="test_cf_server", + name="test_cf", + server_name="test_cf", + alias="test_cf", + transport=MCPTransport.http, + auth_type=MCPAuth(auth_type_value), + # Discovery stamps these onto the in-memory registry entry at build time. + authorization_url="https://provider.com/oauth/authorize", + token_url="https://provider.com/oauth/token", + ) + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: + mock_encrypt.return_value = "mocked_encrypted_state" + + response = await authorize( + request=mock_request, + client_id="dcr_client_id", + mcp_server_name="test_cf", + redirect_uri="http://127.0.0.1:60108/callback", + state="test_state", + ) + + assert response.status_code == 307 + assert "https://provider.com/oauth/authorize" in response.headers["location"] + assert "client_id=dcr_client_id" in response.headers["location"] + + @pytest.mark.asyncio async def test_authorize_endpoint_preserves_existing_query_params(): """Test that authorize endpoint merges OAuth params with existing query params in authorization_url""" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index 12e22de195b..e8fca9ac6ab 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -833,6 +833,39 @@ class TestMCPServerManager: assert spec is not None and isinstance(spec.config, TokenExchangeConfig) assert spec.config.profile == "entra_obo" + @pytest.mark.asyncio + @pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_build_from_table_discovers_upstream_oauth_for_client_forwarded_modes(self, auth_type): + """The gateway's relayed authorize flow (used by the browser-only Authorize) needs the + upstream's authorization_url on the registry entry, and these rows never persist one, so + the DB build must discover it the same way oauth2 rows do.""" + from types import SimpleNamespace + + manager = MCPServerManager() + row = LiteLLM_MCPServerTable( + server_id="cf-db-1", + alias="cf_db", + description="client-forwarded from db", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=auth_type, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + + metadata = SimpleNamespace( + authorization_url="https://idp.example.com/authorize", + token_url="https://idp.example.com/token", + registration_url="https://idp.example.com/register", + scopes=None, + ) + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=metadata)) as mock_discovery: + built = await manager.build_mcp_server_from_table(row, credentials_are_encrypted=False) + + mock_discovery.assert_awaited_once() + assert built.authorization_url == "https://idp.example.com/authorize" + assert built.token_url == "https://idp.example.com/token" + async def _capture_subject_token(self, call) -> Optional[str]: """Run a manager method (via ``call(manager)``) and return the subject_token it threaded into ``_create_mcp_client``.""" diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.test.tsx index 3346bc342f3..2e8fa901f6d 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.test.tsx @@ -91,6 +91,37 @@ describe("MCPToolsViewer auth gate routing", () => { expect(screen.queryByText(GATE_TEXT)).not.toBeInTheDocument(); }); + it.each([["true_passthrough"], ["oauth_delegate"]])( + "shows the Authorize gate for a %s server without a browser token and does not list tools", + async (authType) => { + renderViewer({ auth_type: authType, oauth2_flow: null, delegate_auth_to_upstream: false }); + + expect(await screen.findByText(GATE_TEXT)).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Authorize" })).toBeInTheDocument(); + expect(vi.mocked(listMCPTools)).not.toHaveBeenCalled(); + expect(vi.mocked(getMCPOAuthUserCredentialStatus)).not.toHaveBeenCalled(); + }, + ); + + it.each([["true_passthrough"], ["oauth_delegate"]])( + "forwards the session token via the x-mcp header for a %s server that has one", + async (authType) => { + vi.mocked(isTokenValid).mockReturnValue(true); + vi.mocked(getToken).mockReturnValue({ access_token: "upstream-tok" } as ReturnType); + + renderViewer({ auth_type: authType, oauth2_flow: null, delegate_auth_to_upstream: false }); + + await waitFor(() => + expect(vi.mocked(listMCPTools)).toHaveBeenCalledWith( + "litellm-key", + "srv-1", + expect.objectContaining({ "x-mcp-slack-authorization": "Bearer upstream-tok" }), + ), + ); + expect(screen.queryByText(GATE_TEXT)).not.toBeInTheDocument(); + }, + ); + it("lists tools for an OBO server when the user has a DB credential, with no x-mcp header", async () => { renderViewer({ oauth2_flow: null, delegate_auth_to_upstream: false }); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx index e436732ba3b..6c99a53afaa 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { ToolTestPanel } from "./ToolTestPanel"; import { resolveLogoSrc } from "@/lib/assetPaths"; -import { MCPTool, MCPToolsViewerProps, MCPContent, CallMCPToolResponse, getMcpOAuthMode } from "./types"; +import { AUTH_TYPE, MCPTool, MCPToolsViewerProps, MCPContent, CallMCPToolResponse, getMcpOAuthMode } from "./types"; import { listMCPTools, callMCPTool, getMCPOAuthUserCredentialStatus } from "../networking"; import { isTokenValid, getToken, removeToken } from "@/utils/mcpTokenStore"; import { sanitizeMcpAliasForHeader, buildMcpPassthroughAuthHeader } from "@/utils/mcpHeaderUtils"; @@ -42,19 +42,24 @@ const MCPToolsViewer = ({ // service token and needs no gate. const oauthMode = getMcpOAuthMode({ auth_type, oauth2_flow, delegate_auth_to_upstream }); const isPassthrough = oauthMode === "passthrough"; + // The client-forwarded token modes gate the same way as PKCE passthrough: the + // browser session token (established via the browser-only Authorize in the + // create/edit forms, or right here) is the upstream credential. + const usesBrowserHeldToken = + isPassthrough || auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || auth_type === AUTH_TYPE.OAUTH_DELEGATE; const isAuthorizationCode = oauthMode === "authorization_code"; const [oauthToken, setOauthToken] = useState(() => - isPassthrough && isTokenValid(serverId, userID) ? getToken(serverId, userID)?.access_token ?? null : null, + usesBrowserHeldToken && isTokenValid(serverId, userID) ? getToken(serverId, userID)?.access_token ?? null : null, ); // Re-sync token when serverId/userID changes (useState initializer only runs on mount). useEffect(() => { - if (!isPassthrough) { + if (!usesBrowserHeldToken) { setOauthToken(null); return; } setOauthToken(isTokenValid(serverId, userID) ? getToken(serverId, userID)?.access_token ?? null : null); - }, [serverId, userID, isPassthrough]); + }, [serverId, userID, usesBrowserHeldToken]); const { startOAuthFlow, @@ -109,7 +114,7 @@ const MCPToolsViewer = ({ // x-mcp-{alias}-{header} pattern and forwards it to the upstream MCP server. // When no alias is available, fall back to x-mcp-auth (legacy but still supported). // Passthrough only: authorization_code/token_exchange/M2M tokens are attached server-side, not from the browser. - if (isPassthrough && oauthToken) { + if (usesBrowserHeldToken && oauthToken) { Object.assign(customHeaders, buildMcpPassthroughAuthHeader(serverAlias, oauthToken)); } @@ -164,7 +169,8 @@ const MCPToolsViewer = ({ // Passthrough blocks until a browser session token exists; authorization_code blocks until // the user has a valid DB credential (else the backend returns no tools). enabled: - !!accessToken && (isPassthrough ? oauthToken !== null : isAuthorizationCode ? hasAuthorizationCodeCred : true), + !!accessToken && + (usesBrowserHeldToken ? oauthToken !== null : isAuthorizationCode ? hasAuthorizationCodeCred : true), staleTime: 30000, // Consider data fresh for 30 seconds retry: (failureCount, error: any) => { // Don't retry on 401 — token is invalid, user must re-authenticate @@ -253,7 +259,8 @@ const MCPToolsViewer = ({ // passthrough needs a browser token; authorization_code needs a stored DB credential or a // still-valid one — a 401 from the list call means the backend has none even // after attempting a refresh, so re-authorization is required. - const authGateActive = (isPassthrough && !oauthToken) || authorizationCodeNeedsAuth || authorizationCodeTokenRejected; + const authGateActive = + (usesBrowserHeldToken && !oauthToken) || authorizationCodeNeedsAuth || authorizationCodeTokenRejected; // Treat authorization_code credential-status loading as "tools loading" so the empty state // doesn't flash before we know whether the user needs to authorize. const toolsAreaLoading = isLoadingTools || authorizationCodeStatusLoading; @@ -359,7 +366,7 @@ const MCPToolsViewer = ({ {/* Passthrough auth gate — browser session token absent */} - {isPassthrough && !oauthToken && ( + {usesBrowserHeldToken && !oauthToken && (

Authentication required

From 74a15c21ae2324af75f786b748aff9313e609f8e Mon Sep 17 00:00:00 2001 From: Tin Date: Tue, 7 Jul 2026 22:45:48 -0700 Subject: [PATCH 052/399] fix(mcp): run upstream OAuth endpoint discovery for config-defined client-forwarded servers The DB build already discovers authorization/token endpoints for true_passthrough and oauth_delegate rows; the config.yaml load path kept the oauth2-only gate, so a YAML-defined server in either mode could not use the relayed authorize flow unless the YAML declared authorization_url. Both paths now share the same auth type set. --- litellm/proxy/_experimental/mcp_server/mcp_server_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index c8681b94f5e..dd7d3e96262 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -983,8 +983,9 @@ class MCPServerManager: ) auth_type = server_config.get("auth_type", None) + config_upstream_oauth_auth_types = (MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate) if server_url and ( - auth_type == MCPAuth.oauth2 + auth_type in config_upstream_oauth_auth_types or self._obo_needs_endpoint_discovery( auth_type, server_config.get("token_exchange_endpoint"), @@ -993,7 +994,7 @@ class MCPServerManager: ): mcp_oauth_metadata = await self._descovery_metadata( server_url=server_url, - allow_origin_fallback=auth_type == MCPAuth.oauth2, + allow_origin_fallback=auth_type in config_upstream_oauth_auth_types, ) else: mcp_oauth_metadata = None From 98818df418f4e613e510500c91ad1a86a12c1d6c Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 14:28:19 -0700 Subject: [PATCH 053/399] fix(mcp): recognize per-server auth header at connect and stop persisting browser-authorize tokens Two correctness fixes for the client-forwarded token modes. The preemptive-401 connect gate for true_passthrough and oauth_delegate only inspected the request-wide Authorization, so a caller who bound the upstream token via the per-server x-mcp-{alias}-authorization header (the mandatory shape in a multi-server aggregate, where the request-wide Authorization is withheld) was spuriously 401'd at connect even though egress already honors that header. The gate now recognizes the per-server header for both modes via a shared helper, mode-correctly: true_passthrough treats any Authorization or the per-server header as the upstream token, oauth_delegate keeps requiring a distinct x-litellm-api-key so a lone Authorization consumed for admission is never mistaken for an upstream token. The preemptive raise is also gated to single-server scopes so a multi-server aggregate degrades gracefully (the listing absorbs a per-server failure) instead of one missing token 401-ing the whole connect. The browser-only Authorize flow was writing the upstream access and refresh token to LiteLLM_MCPUserCredentials, contradicting the modes' persist-nothing contract: the temp OAuth-relay server was cached with a hardcoded oauth2 auth_type, so needs_user_oauth_token was true and the token exchange stored it. The create and edit forms now send the real auth_type for these modes, so the temp server is not oauth2, needs_user_oauth_token is false, and the exchange skips storage while still returning the token to the browser session. --- .../mcp_server/test_discoverable_endpoints.py | 79 +++++++++++++++++++ .../mcp_tools/create_mcp_server.tsx | 5 +- .../components/mcp_tools/mcp_server_edit.tsx | 5 +- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 926c3d5a1f4..9de342eabd7 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -6,6 +6,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import HTTPException +from litellm.types.mcp import MCPAuth + # Fixture to mock IP address check for all MCP tests # This prevents tests from failing due to IP-based access control @@ -3403,6 +3405,83 @@ async def test_token_exchange_passes_through_upstream_expires_in(): assert body["expires_in"] == 43200 +async def _exchange_persistence_attempted_for_auth_type(auth_type) -> bool: + """Run exchange_token_with_server for a server of ``auth_type`` and report whether it attempted + to persist the exchanged token server-side. The client-forwarded token modes must not persist: + their contract is that the upstream token stays browser-held, minted/stored/refreshed nowhere.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="t", + name="t", + server_name="t", + alias="t", + transport=MCPTransport.http, + auth_type=auth_type, + client_id="cid", + client_secret="cs", + authorization_url="https://provider.com/oauth/authorize", + token_url="https://provider.com/oauth/token", + ) + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + fake_http_response = MagicMock() + fake_http_response.json.return_value = {"access_token": "tok", "refresh_token": "r", "token_type": "Bearer"} + fake_http_response.raise_for_status = MagicMock() + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock(return_value=fake_http_response) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_user_id_from_request", + new_callable=AsyncMock, + return_value="admin-user", + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._store_per_user_token_server_side", + new_callable=AsyncMock, + ) as mock_store, + ): + await exchange_token_with_server( + request=mock_request, + mcp_server=server, + grant_type="authorization_code", + code="c", + redirect_uri="http://127.0.0.1:3000/cb", + client_id="cid", + client_secret=None, + code_verifier=None, + ) + return mock_store.await_count > 0 + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +async def test_token_exchange_does_not_persist_for_client_forwarded_modes(auth_type): + """The browser-only Authorize for true_passthrough / oauth_delegate must not write the upstream + token to the DB: these modes forward a browser-held token and persist nothing server-side.""" + assert await _exchange_persistence_attempted_for_auth_type(auth_type) is False + + +@pytest.mark.asyncio +async def test_token_exchange_persists_for_oauth2(): + """Guard the test's own discriminator: a genuine oauth2 (authorization_code) server DOES persist, + so the passthrough no-persist assertion above is meaningful and not vacuously true.""" + assert await _exchange_persistence_attempted_for_auth_type(MCPAuth.oauth2) is True + + # ------------------------------------------------------------------- # OBO (token_exchange) Protected Resource Metadata: discovery must name the # JWT-auth issuer the client SSOs with, not the gateway. diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index b123e3397a1..ad7d4530c92 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -184,7 +184,10 @@ const CreateMCPServer: React.FC = ({ description: values.description, url, transport: transport === TRANSPORT.OPENAPI ? "http" : transport, - auth_type: AUTH_TYPE.OAUTH2, + auth_type: + values.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || values.auth_type === AUTH_TYPE.OAUTH_DELEGATE + ? values.auth_type + : AUTH_TYPE.OAUTH2, credentials: values.credentials, authorization_url: values.authorization_url, token_url: values.token_url, diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index ee67ead8121..7d9316e1baa 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -163,7 +163,10 @@ const MCPServerEdit: React.FC = ({ description: values.description || mcpServer.description, url, transport, - auth_type: AUTH_TYPE.OAUTH2, + auth_type: + mcpServer.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || mcpServer.auth_type === AUTH_TYPE.OAUTH_DELEGATE + ? mcpServer.auth_type + : AUTH_TYPE.OAUTH2, credentials: values.credentials, mcp_access_groups: values.mcp_access_groups || mcpServer.mcp_access_groups, static_headers: staticHeaders, From 1693761a51ef0e6481a784088d44f901fbc1cc2c Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 14:34:20 -0700 Subject: [PATCH 054/399] feat(mcp): record auth_mode and upstream resource on MCP tool-call logs Adds mcp_auth_mode and mcp_server_resource to StandardLoggingMCPToolCall so a relayed passthrough/delegate request can be attributed in an audit to its mode and its upstream target without logging any credential. Both are non-sensitive metadata derived from the resolved server; the admission and upstream tokens stay SecretStr and are never logged. --- litellm/proxy/_experimental/mcp_server/server.py | 2 ++ litellm/types/utils.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 55a0fa083c0..5c814774fda 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -3064,6 +3064,8 @@ if MCP_AVAILABLE: mcp_server_logo_url=mcp_info.get("logo_url"), namespaced_tool_name=namespaced_tool_name, mcp_session_id=session_id, + mcp_auth_mode=mcp_server.auth_type, + mcp_server_resource=mcp_server.url, ) else: return StandardLoggingMCPToolCall( diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 908f5b76424..e71c42084d1 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -2523,6 +2523,20 @@ class StandardLoggingMCPToolCall(TypedDict, total=False): the client is driving a stateful session. Absent for stateless calls. """ + mcp_auth_mode: Optional[str] + """ + The server's auth_type for this call (e.g. `true_passthrough`, `oauth_delegate`, + `oauth2`). For the client-forwarded token modes this records that the caller's own + upstream token was relayed, so an audit can attribute a relayed request to its mode + without logging any credential. + """ + + mcp_server_resource: Optional[str] + """ + The upstream MCP server URL (the RFC 8707 resource) the tool call was forwarded to. + Records which upstream received a relayed request; never a credential. + """ + class StandardLoggingVectorStoreRequest(TypedDict, total=False): """ From 7c52cde5057131469fccd1d8f6625c81c9b5d7d9 Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 15:20:07 -0700 Subject: [PATCH 055/399] fix(mcp): redact upstream URL in tool-call logs and plug fan-out Authorization bypass Two review findings on the passthrough modes. The tool-call log records the upstream MCP server URL as mcp_server_resource, which is persisted in spend-log metadata and sent to logging callbacks. A URL carrying embedded userinfo or a secret query parameter would leak into logs, so the value is now redacted to its bare resource identifier (scheme + host + path); userinfo, query string, and fragment are stripped before it is logged. The listing fan-out withholds the request-wide Authorization from a true_passthrough / oauth_delegate server when another server in scope also consumes it, so one bearer is not replayed across upstreams. The later server.extra_headers copy loop did not honor that decision: a server listing Authorization in extra_headers would re-copy the withheld bearer from raw_headers. The withhold decision is now computed once and applied to both the forwarding branch and the extra_headers loop. --- .../proxy/_experimental/mcp_server/server.py | 23 ++++++++++++++++++- litellm/types/utils.py | 4 +++- .../mcp_server/test_mcp_server.py | 22 ++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 5c814774fda..938cc2bc43b 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -27,6 +27,7 @@ from typing import ( Union, cast, ) +from urllib.parse import urlsplit, urlunsplit import httpx from fastapi import FastAPI, HTTPException @@ -105,6 +106,26 @@ _MAX_STATEFUL_SESSIONS_PER_OWNER = 100 _MCP_ROUTING_PEEK_MAX_BYTES = 4096 +def _redact_mcp_resource_url(url: Optional[str]) -> Optional[str]: + """Reduce an MCP server URL to its bare resource identifier for logging. + + Keeps scheme, host, and path; drops userinfo (``user:pass@``), the query string, + and the fragment, so an upstream URL carrying an embedded token, userinfo, or a + secret query parameter never reaches spend-log metadata or logging callbacks. + Returns None when the URL has no host to identify (nothing safe to log). + """ + if not isinstance(url, str) or not url: + return None + try: + parts = urlsplit(url) + except ValueError: + return None + if not parts.hostname: + return None + netloc = f"{parts.hostname}:{parts.port}" if parts.port else parts.hostname + return urlunsplit((parts.scheme, netloc, parts.path, "", "")) or None + + def _invalidate_byok_cred_cache(user_id: str, server_id: str) -> None: """Remove a (user_id, server_id) entry from the BYOK credential cache. @@ -3065,7 +3086,7 @@ if MCP_AVAILABLE: namespaced_tool_name=namespaced_tool_name, mcp_session_id=session_id, mcp_auth_mode=mcp_server.auth_type, - mcp_server_resource=mcp_server.url, + mcp_server_resource=_redact_mcp_resource_url(mcp_server.url), ) else: return StandardLoggingMCPToolCall( diff --git a/litellm/types/utils.py b/litellm/types/utils.py index e71c42084d1..6b99cfa3314 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -2533,7 +2533,9 @@ class StandardLoggingMCPToolCall(TypedDict, total=False): mcp_server_resource: Optional[str] """ - The upstream MCP server URL (the RFC 8707 resource) the tool call was forwarded to. + The upstream MCP server resource identifier (scheme + host + path) the tool call was + forwarded to. Redacted for logging: userinfo, query string, and fragment are stripped so an + upstream URL carrying an embedded token or secret query parameter never reaches log metadata. Records which upstream received a relayed request; never a credential. """ diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index e9dccdcd4ad..0db36e75e48 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -6854,3 +6854,25 @@ async def test_call_tool_with_legacy_db_m2m_server_resolves_oauth2_flow(): resolved = captured_servers["allowed"] assert resolved and resolved[0].oauth2_flow == "client_credentials" assert resolved[0].has_client_credentials is True + + +@pytest.mark.parametrize( + "url, expected", + [ + # userinfo + secret query param must both be stripped from the logged resource + ("https://user:s3cr3t@mcp.example.com/mcp?token=abcd1234&x=1", "https://mcp.example.com/mcp"), + ("https://mcp.example.com/mcp#frag", "https://mcp.example.com/mcp"), + ("https://host:8443/a/b?q=1", "https://host:8443/a/b"), + ("https://mcp.notion.com/mcp", "https://mcp.notion.com/mcp"), + (None, None), + ("", None), + ("not a url", None), + ], +) +def test_redact_mcp_resource_url_strips_credentials(url, expected): + """The MCP tool-call log records the upstream resource, so the URL must be redacted to + scheme+host+path: userinfo, query string, and fragment (which can carry embedded tokens or + secret parameters) must never reach spend-log metadata or logging callbacks.""" + from litellm.proxy._experimental.mcp_server.server import _redact_mcp_resource_url + + assert _redact_mcp_resource_url(url) == expected From b62b30bac0d575425da7e484cbd4f93360847b6a Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 16:10:30 -0700 Subject: [PATCH 056/399] fix(ui): edit-form browser-authorize payload uses the selected auth_type The edit form's getTemporaryPayload read the server's stored auth_type instead of the value the admin selected in the dropdown, so an admin who switched an existing oauth2 server to true_passthrough (or oauth_delegate) and ran the browser authorize flow built the temporary OAuth-relay server as oauth2. That made needs_user_oauth_token true and persisted the token to the DB, contrary to the mode's browser-held contract, and left it inconsistent with onTokenReceived and the submit payload, both of which already read the form value. It now reads values.auth_type, matching the create form. --- .../mcp_tools/mcp_server_edit.test.tsx | 44 ++++++++++++++++--- .../components/mcp_tools/mcp_server_edit.tsx | 4 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index f071e247466..0579a3208d1 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -19,14 +19,20 @@ vi.mock("../molecules/notifications_manager", () => ({ }, })); -const mockOauth: { tokenResponse: any } = { tokenResponse: null }; +const mockOauth: { + tokenResponse: any; + getTemporaryPayload: (() => Record | null) | null; +} = { tokenResponse: null, getTemporaryPayload: null }; vi.mock("@/hooks/useMcpOAuthFlow", () => ({ - useMcpOAuthFlow: () => ({ - startOAuthFlow: vi.fn(), - status: "idle", - error: null, - tokenResponse: mockOauth.tokenResponse, - }), + useMcpOAuthFlow: (opts: { getTemporaryPayload?: () => Record | null }) => { + mockOauth.getTemporaryPayload = opts?.getTemporaryPayload ?? null; + return { + startOAuthFlow: vi.fn(), + status: "idle", + error: null, + tokenResponse: mockOauth.tokenResponse, + }; + }, })); vi.mock("./mcp_server_cost_config", () => ({ @@ -344,6 +350,30 @@ describe("MCPServerEdit (true passthrough warning)", () => { screen.queryByText("True Passthrough disables LiteLLM authentication for this server"), ).not.toBeInTheDocument(); }); + + it("browser-authorize temp payload uses the selected auth_type, not the stored one", async () => { + // Stored server is oauth2; the admin switches the dropdown to true_passthrough before saving. + // The temp OAuth-relay payload must reflect the selection so the exchange is treated as + // browser-held (no DB persistence), matching onTokenReceived and the create form. + render( + , + ); + + await selectAntOption("Authentication", "True Passthrough (no LiteLLM auth)"); + + await waitFor(() => { + expect(mockOauth.getTemporaryPayload).toBeTruthy(); + }); + const payload = mockOauth.getTemporaryPayload!(); + expect(payload).toBeTruthy(); + expect(payload?.auth_type).toBe("true_passthrough"); + }); }); describe("MCPServerEdit (auth type switch)", () => { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 7d9316e1baa..0a00f05ab53 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -164,8 +164,8 @@ const MCPServerEdit: React.FC = ({ url, transport, auth_type: - mcpServer.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || mcpServer.auth_type === AUTH_TYPE.OAUTH_DELEGATE - ? mcpServer.auth_type + values.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || values.auth_type === AUTH_TYPE.OAUTH_DELEGATE + ? values.auth_type : AUTH_TYPE.OAUTH2, credentials: values.credentials, mcp_access_groups: values.mcp_access_groups || mcpServer.mcp_access_groups, From ee5a0651161b8c71a5852e2590e6c9f9285f937b Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 17:39:01 -0700 Subject: [PATCH 057/399] refactor(ui): extract isClientForwardedTokenMode helper for the pass-through modes The 'auth_type is true_passthrough or oauth_delegate' check was duplicated inline across both server forms' browser-authorize temp payloads, the edit form's onTokenReceived and tool-preview gate, PassthroughAuthorizeSection, and mcp_tools' usesBrowserHeldToken. Extracted a single isClientForwardedTokenMode helper in types.tsx and routed every site through it so the set of client-forwarded modes lives in one place and cannot drift. Also replaced a pre-existing nested ternary in the authorize button label surfaced by touching the file. --- ui/litellm-dashboard/eslint-metrics.json | 2 +- .../mcp_tools/PassthroughAuthorizeSection.tsx | 15 ++++++++------- .../components/mcp_tools/create_mcp_server.tsx | 6 ++---- .../src/components/mcp_tools/mcp_server_edit.tsx | 11 ++++------- .../src/components/mcp_tools/mcp_tools.tsx | 12 +++++++++--- .../src/components/mcp_tools/types.tsx | 7 +++++++ 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index 37bad071081..e4647b33d61 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,7 +1,7 @@ { "@typescript-eslint/no-explicit-any": 1982, "complexity": 128, - "local/no-large-inline-object-arg": 519, + "local/no-large-inline-object-arg": 513, "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 diff --git a/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx b/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx index 453e3024000..af81f2713ae 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/PassthroughAuthorizeSection.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Button, Form, Input } from "antd"; -import { AUTH_TYPE } from "./types"; +import { isClientForwardedTokenMode } from "./types"; interface PassthroughOAuthFlow { startOAuthFlow: () => void | Promise; @@ -26,7 +26,12 @@ export default function PassthroughAuthorizeSection({ authType?: string | null; oauthFlow: PassthroughOAuthFlow; }) { - if (authType !== AUTH_TYPE.TRUE_PASSTHROUGH && authType !== AUTH_TYPE.OAUTH_DELEGATE) return null; + if (!isClientForwardedTokenMode(authType)) return null; + const authorizeButtonLabels: Record = { + authorizing: "Waiting for authorization...", + exchanging: "Exchanging authorization code...", + }; + const authorizeButtonLabel = authorizeButtonLabels[oauthFlow.status] ?? "Authorize & Fetch Tools (browser-only)"; return (

@@ -57,11 +62,7 @@ export default function PassthroughAuthorizeSection({ onClick={oauthFlow.startOAuthFlow} disabled={oauthFlow.status === "authorizing" || oauthFlow.status === "exchanging"} > - {oauthFlow.status === "authorizing" - ? "Waiting for authorization..." - : oauthFlow.status === "exchanging" - ? "Exchanging authorization code..." - : "Authorize & Fetch Tools (browser-only)"} + {authorizeButtonLabel} {oauthFlow.error &&

{oauthFlow.error}

} {oauthFlow.status === "success" && oauthFlow.tokenResponse?.access_token && ( diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index ad7d4530c92..7d160407d02 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -14,6 +14,7 @@ import { getMcpOAuthMode, MCP_OAUTH2_FLOW_M2M, MCP_OAUTH2_FLOW_INTERACTIVE, + isClientForwardedTokenMode, } from "./types"; import OAuthFormFields from "./OAuthFormFields"; import TruePassthroughWarning from "./TruePassthroughWarning"; @@ -184,10 +185,7 @@ const CreateMCPServer: React.FC = ({ description: values.description, url, transport: transport === TRANSPORT.OPENAPI ? "http" : transport, - auth_type: - values.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || values.auth_type === AUTH_TYPE.OAUTH_DELEGATE - ? values.auth_type - : AUTH_TYPE.OAUTH2, + auth_type: isClientForwardedTokenMode(values.auth_type) ? values.auth_type : AUTH_TYPE.OAUTH2, credentials: values.credentials, authorization_url: values.authorization_url, token_url: values.token_url, diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 0a00f05ab53..2c43b9cb056 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -4,6 +4,7 @@ import { InfoCircleOutlined } from "@ant-design/icons"; import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react"; import { AUTH_TYPE, + isClientForwardedTokenMode, OAUTH_FLOW, MCP_OAUTH2_FLOW_M2M, MCP_OAUTH2_FLOW_INTERACTIVE, @@ -163,10 +164,7 @@ const MCPServerEdit: React.FC = ({ description: values.description || mcpServer.description, url, transport, - auth_type: - values.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || values.auth_type === AUTH_TYPE.OAUTH_DELEGATE - ? values.auth_type - : AUTH_TYPE.OAUTH2, + auth_type: isClientForwardedTokenMode(values.auth_type) ? values.auth_type : AUTH_TYPE.OAUTH2, credentials: values.credentials, mcp_access_groups: values.mcp_access_groups || mcpServer.mcp_access_groups, static_headers: staticHeaders, @@ -181,7 +179,7 @@ const MCPServerEdit: React.FC = ({ } const effectiveAuthType = form.getFieldValue("auth_type") ?? mcpServer.auth_type; - if (effectiveAuthType === AUTH_TYPE.TRUE_PASSTHROUGH || effectiveAuthType === AUTH_TYPE.OAUTH_DELEGATE) { + if (isClientForwardedTokenMode(effectiveAuthType)) { setToken( mcpServer.server_id, { @@ -393,8 +391,7 @@ const MCPServerEdit: React.FC = ({ oauth2_flow: mcpServer.oauth2_flow, delegate_auth_to_upstream: mcpServer.delegate_auth_to_upstream, }) === "passthrough"; - const isBrowserHeldTokenMode = - mcpServer.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || mcpServer.auth_type === AUTH_TYPE.OAUTH_DELEGATE; + const isBrowserHeldTokenMode = isClientForwardedTokenMode(mcpServer.auth_type); if (isPassthrough || isBrowserHeldTokenMode) { const token = oauthTokenResponse?.access_token ?? diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx index 6c99a53afaa..928a2e3c6bb 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx @@ -2,7 +2,14 @@ import React, { useCallback, useEffect, useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { ToolTestPanel } from "./ToolTestPanel"; import { resolveLogoSrc } from "@/lib/assetPaths"; -import { AUTH_TYPE, MCPTool, MCPToolsViewerProps, MCPContent, CallMCPToolResponse, getMcpOAuthMode } from "./types"; +import { + isClientForwardedTokenMode, + MCPTool, + MCPToolsViewerProps, + MCPContent, + CallMCPToolResponse, + getMcpOAuthMode, +} from "./types"; import { listMCPTools, callMCPTool, getMCPOAuthUserCredentialStatus } from "../networking"; import { isTokenValid, getToken, removeToken } from "@/utils/mcpTokenStore"; import { sanitizeMcpAliasForHeader, buildMcpPassthroughAuthHeader } from "@/utils/mcpHeaderUtils"; @@ -45,8 +52,7 @@ const MCPToolsViewer = ({ // The client-forwarded token modes gate the same way as PKCE passthrough: the // browser session token (established via the browser-only Authorize in the // create/edit forms, or right here) is the upstream credential. - const usesBrowserHeldToken = - isPassthrough || auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || auth_type === AUTH_TYPE.OAUTH_DELEGATE; + const usesBrowserHeldToken = isPassthrough || isClientForwardedTokenMode(auth_type); const isAuthorizationCode = oauthMode === "authorization_code"; const [oauthToken, setOauthToken] = useState(() => usesBrowserHeldToken && isTokenValid(serverId, userID) ? getToken(serverId, userID)?.access_token ?? null : null, diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx index 70cc8129bf1..dca9e574e22 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx @@ -45,6 +45,13 @@ export const AUTH_TYPE = { OAUTH_DELEGATE: "oauth_delegate", }; +// The two client-forwarded token modes: the caller supplies the upstream Authorization (forwarded +// verbatim for true_passthrough, alongside LiteLLM admission for oauth_delegate). The dashboard holds +// their token in sessionStorage instead of persisting it, and the browser-authorize temp payload keeps +// their real auth_type so the backend does not treat them as needing a stored per-user token. +export const isClientForwardedTokenMode = (authType?: string | null): boolean => + authType === AUTH_TYPE.TRUE_PASSTHROUGH || authType === AUTH_TYPE.OAUTH_DELEGATE; + export const OAUTH_FLOW = { INTERACTIVE: "interactive", M2M: "m2m", From a199bf975d588754a33331173dc4a406428a5e51 Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 17:46:28 -0700 Subject: [PATCH 058/399] refactor(mcp): share one constant for the upstream-OAuth discovery auth types The config-YAML loader and the DB loader each defined their own local tuple (oauth2, true_passthrough, oauth_delegate) to decide which auth types trigger upstream OAuth endpoint discovery, under two different names. Hoisted them to a single module constant _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES so the two load paths cannot drift on which modes get discovery. --- .../mcp_server/mcp_server_manager.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index dd7d3e96262..356ed7a2729 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -171,6 +171,16 @@ _user_env_vars_cache: dict[tuple[str, str], tuple[dict[str, str], float]] = {} _USER_ENV_VARS_CACHE_TTL = 60 # seconds _USER_ENV_VARS_CACHE_MAX_SIZE = 4096 # cap to prevent unbounded growth +# Auth types whose upstream OAuth endpoints (protected-resource + authorization-server metadata) the +# gateway discovers from the upstream itself: interactive oauth2 and the two client-forwarded modes. +# OBO/M2M endpoint discovery is decided separately via _obo_needs_endpoint_discovery. Shared by the +# config-YAML and DB server loaders so the two paths cannot drift on which modes trigger discovery. +_UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES: tuple[MCPAuth, ...] = ( + MCPAuth.oauth2, + MCPAuth.true_passthrough, + MCPAuth.oauth_delegate, +) + def invalidate_user_env_vars_cache(user_id: str, server_id: str) -> None: """Drop a cached entry after the user stores or clears their env var values @@ -983,9 +993,8 @@ class MCPServerManager: ) auth_type = server_config.get("auth_type", None) - config_upstream_oauth_auth_types = (MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate) if server_url and ( - auth_type in config_upstream_oauth_auth_types + auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES or self._obo_needs_endpoint_discovery( auth_type, server_config.get("token_exchange_endpoint"), @@ -994,7 +1003,7 @@ class MCPServerManager: ): mcp_oauth_metadata = await self._descovery_metadata( server_url=server_url, - allow_origin_fallback=auth_type in config_upstream_oauth_auth_types, + allow_origin_fallback=auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, ) else: mcp_oauth_metadata = None @@ -1385,9 +1394,8 @@ class MCPServerManager: auth_type = cast(MCPAuthType, mcp_server.auth_type) server_url = mcp_server.url - upstream_oauth_auth_types = (MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate) needs_discovery = bool(server_url) and ( - (auth_type in upstream_oauth_auth_types and not mcp_server.authorization_url) + (auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES and not mcp_server.authorization_url) or self._obo_needs_endpoint_discovery( auth_type, mcp_server.token_exchange_endpoint @@ -1398,7 +1406,7 @@ class MCPServerManager: mcp_oauth_metadata = ( await self._descovery_metadata( server_url=server_url, # type: ignore[arg-type] - allow_origin_fallback=auth_type in upstream_oauth_auth_types, + allow_origin_fallback=auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, ) if needs_discovery else None From e29e24e628661e1faf75955e66d185386ddf5a4d Mon Sep 17 00:00:00 2001 From: Tin Date: Wed, 8 Jul 2026 23:09:19 -0700 Subject: [PATCH 059/399] fix(ui): keep the browser-authorized token out of form.credentials for the pass-through modes The create form wrote the upstream token obtained by Authorize & Fetch into form.credentials for every mode, so for true_passthrough / oauth_delegate the browser-held token leaked into the OAuth flow's getCredentials (preview requests) and the redirect-persist cache, and was a step away from server-level credential persistence. onTokenReceived now early-returns for the client-forwarded modes, holding the token only in local state for preview (mirroring the edit form), instead of writing it into form.credentials. --- .../mcp_tools/create_mcp_server.test.tsx | 25 +++++++++++ .../mcp_tools/create_mcp_server.tsx | 45 ++++++++++++------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index ca94aae20e9..eecbc253b6b 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -30,6 +30,7 @@ const oauthHook = vi.hoisted(() => ({ onTokenReceived: null as | ((token: Record | null, registeredClient?: { clientId?: string; clientSecret?: string }) => void) | null, + getCredentials: null as (() => Record | undefined) | null, })); vi.mock("@/hooks/useMcpOAuthFlow", () => ({ useMcpOAuthFlow: (opts: { @@ -37,8 +38,10 @@ vi.mock("@/hooks/useMcpOAuthFlow", () => ({ token: Record | null, registeredClient?: { clientId?: string; clientSecret?: string }, ) => void; + getCredentials?: () => Record | undefined; }) => { oauthHook.onTokenReceived = opts.onTokenReceived; + oauthHook.getCredentials = opts.getCredentials ?? null; return { startOAuthFlow: vi.fn(), status: "idle", @@ -349,6 +352,28 @@ describe("CreateMCPServer", () => { expect(payload.credentials).toEqual({ auth_value: "my-secret-key" }); }); + it("does not write the browser-authorized token into form.credentials for true_passthrough", async () => { + await selectHttpTransport(); + + const user = userEvent.setup({ delay: null }); + await user.type(getServerNameInput(), "PT_Server"); + await user.type(screen.getByPlaceholderText("https://your-mcp-server.com"), "https://example.com/mcp"); + + await selectAntOption("Authentication", "True Passthrough (no LiteLLM auth)"); + + // Simulate the browser Authorize & Fetch flow handing back an upstream token. + await waitFor(() => expect(oauthHook.onTokenReceived).toBeTruthy()); + await act(async () => { + oauthHook.onTokenReceived!({ access_token: "upstream-tok", token_type: "Bearer" }, undefined); + }); + + // For a browser-only mode the token must never land in form.credentials, which the OAuth flow's + // getCredentials reads for preview requests and the redirect-persist cache serializes. Without + // the guard, onTokenReceived writes it here and this returns { access_token: "upstream-tok" }. + const credentials = oauthHook.getCredentials?.() ?? {}; + expect(credentials.access_token).toBeUndefined(); + }); + it("should not show auth value field when None auth type is selected", async () => { await selectHttpTransport(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 7d160407d02..00eda92ba25 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -200,23 +200,36 @@ const CreateMCPServer: React.FC = ({ onTokenReceived: (token, registeredClient) => { setOauthAccessToken(token?.access_token ?? null); - if (token?.access_token) { - const credentials = { - access_token: token.access_token, - ...(token.refresh_token && { refresh_token: token.refresh_token }), - ...(token.expires_in && { expires_in: token.expires_in }), - ...(token.scope && { scope: token.scope }), - ...(registeredClient?.clientId && { client_id: registeredClient.clientId }), - ...(registeredClient?.clientSecret && { client_secret: registeredClient.clientSecret }), - }; - - form.setFieldsValue({ credentials }); - setAuthorizedUrl(getOAuthAuthorizationTarget(form.getFieldsValue(true))); - - NotificationsManager.success( - "OAuth authorization successful! Please click 'Create MCP Server' to save the configuration.", - ); + if (!token?.access_token) { + return; } + + if (isClientForwardedTokenMode(form.getFieldValue("auth_type"))) { + // Browser-only modes: the token is held in local state (oauthAccessToken) for tool preview + // and committed to sessionStorage on submit; it must never be written into form.credentials, + // which would persist it as server-level credentials on the created server row. Mirrors the + // edit form's onTokenReceived early return. + NotificationsManager.success( + "Token held for this browser session. Tools can now be previewed and configured; nothing will be saved to LiteLLM.", + ); + return; + } + + const credentials = { + access_token: token.access_token, + ...(token.refresh_token && { refresh_token: token.refresh_token }), + ...(token.expires_in && { expires_in: token.expires_in }), + ...(token.scope && { scope: token.scope }), + ...(registeredClient?.clientId && { client_id: registeredClient.clientId }), + ...(registeredClient?.clientSecret && { client_secret: registeredClient.clientSecret }), + }; + + form.setFieldsValue({ credentials }); + setAuthorizedUrl(getOAuthAuthorizationTarget(form.getFieldsValue(true))); + + NotificationsManager.success( + "OAuth authorization successful! Please click 'Create MCP Server' to save the configuration.", + ); }, onBeforeRedirect: persistCreateUiState, flowSource: "create", From a3f1873a8791db24e85b5db1266b0e06f8f2f6f3 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 11:04:15 -0700 Subject: [PATCH 060/399] fix(ui): extract inline object args in the MCP forms The create/edit forms passed several large object literals inline as arguments (persist-state JSON.stringify, storeMCPOAuthUserCredential, setToken, transport-clear setFieldsValue), tripping local/no-large-inline-object-arg. Assigned each to a named variable at the call site - a pure, behavior-preserving refactor verified by the create/edit suites - which lowers the whole-tree count so the eslint baseline is 512 rather than being raised to accommodate them. --- ui/litellm-dashboard/eslint-metrics.json | 2 +- .../mcp_tools/create_mcp_server.tsx | 48 +++++++++--------- .../components/mcp_tools/mcp_server_edit.tsx | 49 +++++++++---------- 3 files changed, 46 insertions(+), 53 deletions(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index e4647b33d61..ded6ab97e1e 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,7 +1,7 @@ { "@typescript-eslint/no-explicit-any": 1982, "complexity": 128, - "local/no-large-inline-object-arg": 513, + "local/no-large-inline-object-arg": 512, "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 00eda92ba25..0b39add234c 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -137,20 +137,18 @@ const CreateMCPServer: React.FC = ({ } try { const values = form.getFieldsValue(true); - setSecureItem( - CREATE_OAUTH_UI_STATE_KEY, - JSON.stringify({ - modalVisible: isModalVisible, - formValues: values, - transportType, - costConfig, - allowedTools, - hasToolAllowlistInteraction, - searchValue, - aliasManuallyEdited, - logoUrl, - }), - ); + const uiState = { + modalVisible: isModalVisible, + formValues: values, + transportType, + costConfig, + allowedTools, + hasToolAllowlistInteraction, + searchValue, + aliasManuallyEdited, + logoUrl, + }; + setSecureItem(CREATE_OAUTH_UI_STATE_KEY, JSON.stringify(uiState)); } catch (err) { console.warn("Failed to persist MCP create state", err); } @@ -510,23 +508,21 @@ const CreateMCPServer: React.FC = ({ }); if (oauthMode === "authorization_code") { const scope = oauthTokenResponse.scope; - await storeMCPOAuthUserCredential(accessToken, response.server_id, { + const oauthCredentialPayload = { access_token: oauthTokenResponse.access_token, refresh_token: oauthTokenResponse.refresh_token, expires_in: oauthTokenResponse.expires_in, scopes: typeof scope === "string" && scope ? scope.split(" ") : undefined, - }); + }; + await storeMCPOAuthUserCredential(accessToken, response.server_id, oauthCredentialPayload); } else { - setToken( - response.server_id, - { - access_token: oauthTokenResponse.access_token, - expires_in: oauthTokenResponse.expires_in, - refresh_token: oauthTokenResponse.refresh_token, - token_type: oauthTokenResponse.token_type, - }, - userID, - ); + const browserHeldToken = { + access_token: oauthTokenResponse.access_token, + expires_in: oauthTokenResponse.expires_in, + refresh_token: oauthTokenResponse.refresh_token, + token_type: oauthTokenResponse.token_type, + }; + setToken(response.server_id, browserHeldToken, userID); } } diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 2c43b9cb056..59d7b28aacb 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -180,16 +180,13 @@ const MCPServerEdit: React.FC = ({ const effectiveAuthType = form.getFieldValue("auth_type") ?? mcpServer.auth_type; if (isClientForwardedTokenMode(effectiveAuthType)) { - setToken( - mcpServer.server_id, - { - access_token: token.access_token, - expires_in: token.expires_in, - refresh_token: token.refresh_token, - token_type: token.token_type, - }, - userID, - ); + const browserHeldToken = { + access_token: token.access_token, + expires_in: token.expires_in, + refresh_token: token.refresh_token, + token_type: token.token_type, + }; + setToken(mcpServer.server_id, browserHeldToken, userID); NotificationsManager.success( "Token held for this browser session. Tools can now be loaded and configured; nothing was saved to LiteLLM.", ); @@ -467,7 +464,7 @@ const MCPServerEdit: React.FC = ({ const handleTransportChange = (value: string) => { // Clear fields that are not relevant for the selected transport. if (value === "stdio") { - form.setFieldsValue({ + const clearedForStdio = { url: undefined, spec_path: undefined, auth_type: undefined, @@ -475,15 +472,17 @@ const MCPServerEdit: React.FC = ({ authorization_url: undefined, token_url: undefined, registration_url: undefined, - }); + }; + form.setFieldsValue(clearedForStdio); } else if (value === TRANSPORT.OPENAPI) { - form.setFieldsValue({ + const clearedForOpenapi = { url: undefined, command: undefined, args: undefined, env_json: undefined, stdio_config: undefined, - }); + }; + form.setFieldsValue(clearedForOpenapi); } else { form.setFieldsValue({ spec_path: undefined, @@ -761,23 +760,21 @@ const MCPServerEdit: React.FC = ({ try { if (oauthMode === "authorization_code") { const scope = oauthTokenResponse.scope; - await storeMCPOAuthUserCredential(accessToken, mcpServer.server_id, { + const oauthCredentialPayload = { access_token: oauthTokenResponse.access_token, refresh_token: oauthTokenResponse.refresh_token, expires_in: oauthTokenResponse.expires_in, scopes: typeof scope === "string" && scope ? scope.split(" ") : undefined, - }); + }; + await storeMCPOAuthUserCredential(accessToken, mcpServer.server_id, oauthCredentialPayload); } else if (oauthMode === "passthrough") { - setToken( - mcpServer.server_id, - { - access_token: oauthTokenResponse.access_token, - expires_in: oauthTokenResponse.expires_in, - refresh_token: oauthTokenResponse.refresh_token, - token_type: oauthTokenResponse.token_type, - }, - userID, - ); + const browserHeldToken = { + access_token: oauthTokenResponse.access_token, + expires_in: oauthTokenResponse.expires_in, + refresh_token: oauthTokenResponse.refresh_token, + token_type: oauthTokenResponse.token_type, + }; + setToken(mcpServer.server_id, browserHeldToken, userID); } } catch (error: unknown) { const message = error instanceof Error ? error.message : ""; From 0a40bd7ae5e8578eba0c60fe4efb60510eb032f9 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:47:51 -0700 Subject: [PATCH 061/399] fix(ui): prevent reasoning block from expanding chat playground layout (#32485) The expanded reasoning block did not constrain its width or break long unbreakable tokens, so its inline-block bubble grew past its max width and pushed the whole page wider (#32481). Mirror the message body handling by capping the container width and breaking long words/code. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../chat_ui/ReasoningContent.test.tsx | 32 +++++++++++++++++++ .../components/chat_ui/ReasoningContent.tsx | 14 ++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.test.tsx diff --git a/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.test.tsx b/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.test.tsx new file mode 100644 index 00000000000..35540d3ebde --- /dev/null +++ b/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.test.tsx @@ -0,0 +1,32 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import { describe, it, expect } from "vitest"; +import ReasoningContent from "./ReasoningContent"; + +describe("ReasoningContent", () => { + it("should render nothing when reasoningContent is empty", () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); + + it("should show reasoning content expanded by default and toggle on click", () => { + render(); + + expect(screen.getByText("thinking hard")).toBeInTheDocument(); + expect(screen.getByText("Hide reasoning")).toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button")); + + expect(screen.queryByText("thinking hard")).not.toBeInTheDocument(); + expect(screen.getByText("Show reasoning")).toBeInTheDocument(); + }); + + it("should constrain width and break long words so it cannot expand the layout (regression #32481)", () => { + const longToken = "a".repeat(500); + render(); + + const contentBox = screen.getByText(longToken).closest("div.mt-2"); + expect(contentBox).not.toBeNull(); + expect(contentBox).toHaveClass("max-w-full"); + expect(contentBox).toHaveStyle({ wordBreak: "break-word", overflowWrap: "break-word" }); + }); +}); diff --git a/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.tsx b/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.tsx index e537c6de79f..30ba2d3fd95 100644 --- a/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.tsx +++ b/ui/litellm-dashboard/src/components/chat_ui/ReasoningContent.tsx @@ -27,7 +27,10 @@ const ReasoningContent: React.FC = ({ reasoningContent }) {isExpanded && ( -
+
= ({ reasoningContent }) language={match[1]} PreTag="div" className="rounded-md my-2" + wrapLines={true} + wrapLongLines={true} {...props} > {String(children).replace(/\n$/, "")} ) : ( - + {children} ); }, + pre: ({ node, ...props }) =>
,
             }}
           >
             {reasoningContent}

From 4e63c0c9e66618d5a6e2385e966a05d4da5a51ec Mon Sep 17 00:00:00 2001
From: T K Chandra Hasan 
Date: Fri, 10 Jul 2026 00:18:11 +0530
Subject: [PATCH 062/399] Fix enterprise doc link (#31815)

Signed-off-by: T K Chandra Hasan 
---
 enterprise/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/enterprise/README.md b/enterprise/README.md
index f5eb5078e81..c708dad5a06 100644
--- a/enterprise/README.md
+++ b/enterprise/README.md
@@ -6,4 +6,4 @@ Code in this folder is licensed under a commercial license. Please review the [L
 
 👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://enterprise.litellm.ai/demo?month=2024-02)
 
-See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/proxy/enterprise)
+See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/enterprise)

From a874de6ac60a4c4cc940576adaf181bc4ae8494a Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
 <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Thu, 9 Jul 2026 11:51:12 -0700
Subject: [PATCH 063/399] feat(models): add GPT-5.6 (sol/terra/luna) pricing
 and metadata (#32659)

* feat(models): add GPT-5.6 (sol/terra/luna) pricing and metadata

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: allow gpt-5.6 service-tier cache-write keys in model prices schema

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: floating point entry errors

---------

Co-authored-by: mateo 
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
---
 ...odel_prices_and_context_window_backup.json | 212 ++++++++++++++++++
 model_prices_and_context_window.json          | 212 ++++++++++++++++++
 .../llm_cost_calc/test_llm_cost_calc_utils.py |  55 +++++
 .../llms/openai/test_is_model_gpt_5_model.py  |  43 ++++
 .../test_gpt_5_6_model_metadata.py            |  79 +++++++
 tests/test_litellm/test_utils.py              |   3 +
 6 files changed, 604 insertions(+)
 create mode 100644 tests/test_litellm/test_gpt_5_6_model_metadata.py

diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json
index db534b52df9..a111f301d11 100644
--- a/litellm/model_prices_and_context_window_backup.json
+++ b/litellm/model_prices_and_context_window_backup.json
@@ -22273,6 +22273,218 @@
         "supports_xhigh_reasoning_effort": true,
         "supports_minimal_reasoning_effort": true
     },
+    "gpt-5.6": {
+        "cache_creation_input_token_cost": 6.25e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05,
+        "cache_creation_input_token_cost_flex": 3.125e-06,
+        "cache_creation_input_token_cost_priority": 1.25e-05,
+        "cache_read_input_token_cost": 5e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 1e-06,
+        "cache_read_input_token_cost_flex": 2.5e-07,
+        "cache_read_input_token_cost_priority": 1e-06,
+        "input_cost_per_token": 5e-06,
+        "input_cost_per_token_above_272k_tokens": 1e-05,
+        "input_cost_per_token_batches": 2.5e-06,
+        "input_cost_per_token_flex": 2.5e-06,
+        "input_cost_per_token_priority": 1e-05,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 3e-05,
+        "output_cost_per_token_above_272k_tokens": 4.5e-05,
+        "output_cost_per_token_batches": 1.5e-05,
+        "output_cost_per_token_flex": 1.5e-05,
+        "output_cost_per_token_priority": 6e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
+    "gpt-5.6-sol": {
+        "cache_creation_input_token_cost": 6.25e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05,
+        "cache_creation_input_token_cost_flex": 3.125e-06,
+        "cache_creation_input_token_cost_priority": 1.25e-05,
+        "cache_read_input_token_cost": 5e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 1e-06,
+        "cache_read_input_token_cost_flex": 2.5e-07,
+        "cache_read_input_token_cost_priority": 1e-06,
+        "input_cost_per_token": 5e-06,
+        "input_cost_per_token_above_272k_tokens": 1e-05,
+        "input_cost_per_token_batches": 2.5e-06,
+        "input_cost_per_token_flex": 2.5e-06,
+        "input_cost_per_token_priority": 1e-05,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 3e-05,
+        "output_cost_per_token_above_272k_tokens": 4.5e-05,
+        "output_cost_per_token_batches": 1.5e-05,
+        "output_cost_per_token_flex": 1.5e-05,
+        "output_cost_per_token_priority": 6e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
+    "gpt-5.6-terra": {
+        "cache_creation_input_token_cost": 3.125e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 6.25e-06,
+        "cache_creation_input_token_cost_flex": 1.5625e-06,
+        "cache_creation_input_token_cost_priority": 6.25e-06,
+        "cache_read_input_token_cost": 2.5e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 5e-07,
+        "cache_read_input_token_cost_flex": 1.25e-07,
+        "cache_read_input_token_cost_priority": 5e-07,
+        "input_cost_per_token": 2.5e-06,
+        "input_cost_per_token_above_272k_tokens": 5e-06,
+        "input_cost_per_token_batches": 1.25e-06,
+        "input_cost_per_token_flex": 1.25e-06,
+        "input_cost_per_token_priority": 5e-06,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 1.5e-05,
+        "output_cost_per_token_above_272k_tokens": 2.25e-05,
+        "output_cost_per_token_batches": 7.5e-06,
+        "output_cost_per_token_flex": 7.5e-06,
+        "output_cost_per_token_priority": 3e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
+    "gpt-5.6-luna": {
+        "cache_creation_input_token_cost": 1.25e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06,
+        "cache_creation_input_token_cost_flex": 6.25e-07,
+        "cache_creation_input_token_cost_priority": 2.5e-06,
+        "cache_read_input_token_cost": 1e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 2e-07,
+        "cache_read_input_token_cost_flex": 5e-08,
+        "cache_read_input_token_cost_priority": 2e-07,
+        "input_cost_per_token": 1e-06,
+        "input_cost_per_token_above_272k_tokens": 2e-06,
+        "input_cost_per_token_batches": 5e-07,
+        "input_cost_per_token_flex": 5e-07,
+        "input_cost_per_token_priority": 2e-06,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 6e-06,
+        "output_cost_per_token_above_272k_tokens": 9e-06,
+        "output_cost_per_token_batches": 3e-06,
+        "output_cost_per_token_flex": 3e-06,
+        "output_cost_per_token_priority": 1.2e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
     "gpt-5.5": {
         "cache_read_input_token_cost": 5e-07,
         "cache_read_input_token_cost_above_272k_tokens": 1e-06,
diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json
index 363ba9842b0..d6e4a265da0 100644
--- a/model_prices_and_context_window.json
+++ b/model_prices_and_context_window.json
@@ -22431,6 +22431,218 @@
         "supports_xhigh_reasoning_effort": true,
         "supports_minimal_reasoning_effort": true
     },
+    "gpt-5.6": {
+        "cache_creation_input_token_cost": 6.25e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05,
+        "cache_creation_input_token_cost_flex": 3.125e-06,
+        "cache_creation_input_token_cost_priority": 1.25e-05,
+        "cache_read_input_token_cost": 5e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 1e-06,
+        "cache_read_input_token_cost_flex": 2.5e-07,
+        "cache_read_input_token_cost_priority": 1e-06,
+        "input_cost_per_token": 5e-06,
+        "input_cost_per_token_above_272k_tokens": 1e-05,
+        "input_cost_per_token_batches": 2.5e-06,
+        "input_cost_per_token_flex": 2.5e-06,
+        "input_cost_per_token_priority": 1e-05,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 3e-05,
+        "output_cost_per_token_above_272k_tokens": 4.5e-05,
+        "output_cost_per_token_batches": 1.5e-05,
+        "output_cost_per_token_flex": 1.5e-05,
+        "output_cost_per_token_priority": 6e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
+    "gpt-5.6-sol": {
+        "cache_creation_input_token_cost": 6.25e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05,
+        "cache_creation_input_token_cost_flex": 3.125e-06,
+        "cache_creation_input_token_cost_priority": 1.25e-05,
+        "cache_read_input_token_cost": 5e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 1e-06,
+        "cache_read_input_token_cost_flex": 2.5e-07,
+        "cache_read_input_token_cost_priority": 1e-06,
+        "input_cost_per_token": 5e-06,
+        "input_cost_per_token_above_272k_tokens": 1e-05,
+        "input_cost_per_token_batches": 2.5e-06,
+        "input_cost_per_token_flex": 2.5e-06,
+        "input_cost_per_token_priority": 1e-05,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 3e-05,
+        "output_cost_per_token_above_272k_tokens": 4.5e-05,
+        "output_cost_per_token_batches": 1.5e-05,
+        "output_cost_per_token_flex": 1.5e-05,
+        "output_cost_per_token_priority": 6e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
+    "gpt-5.6-terra": {
+        "cache_creation_input_token_cost": 3.125e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 6.25e-06,
+        "cache_creation_input_token_cost_flex": 1.5625e-06,
+        "cache_creation_input_token_cost_priority": 6.25e-06,
+        "cache_read_input_token_cost": 2.5e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 5e-07,
+        "cache_read_input_token_cost_flex": 1.25e-07,
+        "cache_read_input_token_cost_priority": 5e-07,
+        "input_cost_per_token": 2.5e-06,
+        "input_cost_per_token_above_272k_tokens": 5e-06,
+        "input_cost_per_token_batches": 1.25e-06,
+        "input_cost_per_token_flex": 1.25e-06,
+        "input_cost_per_token_priority": 5e-06,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 1.5e-05,
+        "output_cost_per_token_above_272k_tokens": 2.25e-05,
+        "output_cost_per_token_batches": 7.5e-06,
+        "output_cost_per_token_flex": 7.5e-06,
+        "output_cost_per_token_priority": 3e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
+    "gpt-5.6-luna": {
+        "cache_creation_input_token_cost": 1.25e-06,
+        "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06,
+        "cache_creation_input_token_cost_flex": 6.25e-07,
+        "cache_creation_input_token_cost_priority": 2.5e-06,
+        "cache_read_input_token_cost": 1e-07,
+        "cache_read_input_token_cost_above_272k_tokens": 2e-07,
+        "cache_read_input_token_cost_flex": 5e-08,
+        "cache_read_input_token_cost_priority": 2e-07,
+        "input_cost_per_token": 1e-06,
+        "input_cost_per_token_above_272k_tokens": 2e-06,
+        "input_cost_per_token_batches": 5e-07,
+        "input_cost_per_token_flex": 5e-07,
+        "input_cost_per_token_priority": 2e-06,
+        "litellm_provider": "openai",
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "max_tokens": 128000,
+        "mode": "chat",
+        "output_cost_per_token": 6e-06,
+        "output_cost_per_token_above_272k_tokens": 9e-06,
+        "output_cost_per_token_batches": 3e-06,
+        "output_cost_per_token_flex": 3e-06,
+        "output_cost_per_token_priority": 1.2e-05,
+        "regional_processing_uplift_multiplier_eu": 1.1,
+        "regional_processing_uplift_multiplier_us": 1.1,
+        "supported_endpoints": [
+            "/v1/chat/completions",
+            "/v1/batch",
+            "/v1/responses"
+        ],
+        "supported_modalities": [
+            "text",
+            "image"
+        ],
+        "supported_output_modalities": [
+            "text"
+        ],
+        "supports_function_calling": true,
+        "supports_minimal_reasoning_effort": false,
+        "supports_native_streaming": true,
+        "supports_none_reasoning_effort": true,
+        "supports_parallel_function_calling": true,
+        "supports_pdf_input": true,
+        "supports_prompt_caching": true,
+        "supports_reasoning": true,
+        "supports_response_schema": true,
+        "supports_system_messages": true,
+        "supports_tool_choice": true,
+        "supports_vision": true,
+        "supports_web_search": true,
+        "supports_xhigh_reasoning_effort": true
+    },
     "gpt-5.5": {
         "cache_read_input_token_cost": 5e-07,
         "cache_read_input_token_cost_above_272k_tokens": 1e-06,
diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py
index 0e6c6061b46..dcc359ceff4 100644
--- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py
+++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py
@@ -507,6 +507,61 @@ def test_generic_cost_per_token_gpt55_pro():
     )
 
 
+@pytest.mark.parametrize(
+    "model,input_cost,output_cost,cache_read_cost,cache_write_cost",
+    [
+        ("gpt-5.6", 5e-6, 3e-5, 5e-7, 6.25e-6),
+        ("gpt-5.6-sol", 5e-6, 3e-5, 5e-7, 6.25e-6),
+        ("gpt-5.6-terra", 2.5e-6, 1.5e-5, 2.5e-7, 3.125e-6),
+        ("gpt-5.6-luna", 1e-6, 6e-6, 1e-7, 1.25e-6),
+    ],
+)
+def test_generic_cost_per_token_gpt56(
+    model, input_cost, output_cost, cache_read_cost, cache_write_cost
+):
+    """gpt-5.6 (sol/terra/luna): base pricing + new cache-write cost.
+
+    Cache writes are billed at 1.25x the uncached input rate for this family.
+    """
+    custom_llm_provider = "openai"
+    os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
+    litellm.model_cost = litellm.get_model_cost_map(url="")
+
+    model_cost_map = litellm.model_cost[model]
+
+    assert model_cost_map["input_cost_per_token"] == input_cost
+    assert model_cost_map["output_cost_per_token"] == output_cost
+    assert model_cost_map["cache_read_input_token_cost"] == cache_read_cost
+    assert model_cost_map["cache_creation_input_token_cost"] == cache_write_cost
+    assert model_cost_map["litellm_provider"] == "openai"
+    assert model_cost_map["mode"] == "chat"
+    assert model_cost_map["cache_creation_input_token_cost"] == pytest.approx(
+        input_cost * 1.25
+    )
+    assert model_cost_map["max_input_tokens"] == 1050000
+    assert model_cost_map["input_cost_per_token_above_272k_tokens"] == pytest.approx(
+        input_cost * 2
+    )
+    assert model_cost_map["output_cost_per_token_above_272k_tokens"] == pytest.approx(
+        output_cost * 1.5
+    )
+
+    prompt_tokens = 1000
+    completion_tokens = 500
+    usage = Usage(
+        prompt_tokens=prompt_tokens,
+        completion_tokens=completion_tokens,
+        total_tokens=prompt_tokens + completion_tokens,
+    )
+    prompt_cost, completion_cost = generic_cost_per_token(
+        model=model,
+        usage=usage,
+        custom_llm_provider=custom_llm_provider,
+    )
+    assert round(prompt_cost, 10) == round(input_cost * prompt_tokens, 10)
+    assert round(completion_cost, 10) == round(output_cost * completion_tokens, 10)
+
+
 @pytest.mark.parametrize(
     "model,expected_none,expected_xhigh,expected_minimal",
     [
diff --git a/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py b/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py
index 02dd9dade0a..1095819c98c 100644
--- a/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py
+++ b/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py
@@ -50,6 +50,10 @@ GPT5_MODELS = [
     "gpt-5.5-pro",
     "gpt-5.5-2026-04-23",  # dated variant
     "gpt-5.5-pro-2026-04-23",  # dated variant
+    "gpt-5.6",
+    "gpt-5.6-sol",
+    "gpt-5.6-terra",
+    "gpt-5.6-luna",
     "gpt-5.1-chat",  # versioned chat — THE KEY REGRESSION CASE
     "gpt-5.2-chat",  # versioned chat — also a regression case
     "gpt-5.3-chat",  # versioned chat — THE KEY REGRESSION CASE
@@ -112,6 +116,45 @@ class TestOpenAIGPT5ConfigIsModelGpt5Model:
             ), f"Expected '{model}' (gpt-5-chat family) NOT to be on the GPT-5 path"
 
 
+# Models that are gpt-5.4 or newer. main.py gates the automatic switch to the
+# /v1/responses bridge (when reasoning_effort is set and tools are passed) on
+# is_model_gpt_5_4_plus_model, so the gpt-5.6 family must land on the True side.
+GPT5_4_PLUS_MODELS = [
+    "gpt-5.4",
+    "gpt-5.5",
+    "gpt-5.5-pro",
+    "gpt-5.6",
+    "gpt-5.6-sol",
+    "gpt-5.6-terra",
+    "gpt-5.6-luna",
+    "openai/gpt-5.6-sol",
+]
+
+GPT5_PRE_5_4_MODELS = [
+    "gpt-5",
+    "gpt-5.1",
+    "gpt-5.2",
+    "gpt-5.3",
+    "gpt-5.3-chat",
+    "gpt-4o",
+]
+
+
+class TestOpenAIGPT5ConfigIsModelGpt54PlusModel:
+
+    @pytest.mark.parametrize("model", GPT5_4_PLUS_MODELS)
+    def test_gpt5_4_plus_models_are_classified_as_5_4_plus(self, model: str):
+        assert OpenAIGPT5Config.is_model_gpt_5_4_plus_model(
+            model
+        ), f"Expected '{model}' to be classified as gpt-5.4-or-newer"
+
+    @pytest.mark.parametrize("model", GPT5_PRE_5_4_MODELS)
+    def test_pre_5_4_models_are_not_classified_as_5_4_plus(self, model: str):
+        assert not OpenAIGPT5Config.is_model_gpt_5_4_plus_model(
+            model
+        ), f"Expected '{model}' NOT to be classified as gpt-5.4-or-newer"
+
+
 # ---------------------------------------------------------------------------
 # AzureOpenAIGPT5Config
 # ---------------------------------------------------------------------------
diff --git a/tests/test_litellm/test_gpt_5_6_model_metadata.py b/tests/test_litellm/test_gpt_5_6_model_metadata.py
new file mode 100644
index 00000000000..30a0777f477
--- /dev/null
+++ b/tests/test_litellm/test_gpt_5_6_model_metadata.py
@@ -0,0 +1,79 @@
+import json
+from pathlib import Path
+
+import pytest
+
+from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
+
+GPT_5_6_MODELS = ("gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna")
+
+STANDARD_PRICING = {
+    "gpt-5.6": (5e-06, 3e-05, 5e-07, 6.25e-06),
+    "gpt-5.6-sol": (5e-06, 3e-05, 5e-07, 6.25e-06),
+    "gpt-5.6-terra": (2.5e-06, 1.5e-05, 2.5e-07, 3.125e-06),
+    "gpt-5.6-luna": (1e-06, 6e-06, 1e-07, 1.25e-06),
+}
+
+
+@pytest.mark.parametrize("model", GPT_5_6_MODELS)
+def test_openai_gpt_5_6_model_info(model):
+    json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json"
+    with open(json_path) as f:
+        model_cost = json.load(f)
+
+    info = model_cost.get(model)
+    assert info is not None, f"{model} not found in model_prices_and_context_window.json"
+
+    assert info["litellm_provider"] == "openai"
+    assert info["mode"] == "chat"
+
+    input_cost, output_cost, cache_read_cost, cache_write_cost = STANDARD_PRICING[model]
+    assert info["input_cost_per_token"] == input_cost
+    assert info["output_cost_per_token"] == output_cost
+    assert info["cache_read_input_token_cost"] == cache_read_cost
+    assert info["cache_creation_input_token_cost"] == cache_write_cost
+    assert info["cache_creation_input_token_cost"] == pytest.approx(input_cost * 1.25)
+
+    assert info["input_cost_per_token_above_272k_tokens"] == pytest.approx(input_cost * 2)
+    assert info["output_cost_per_token_above_272k_tokens"] == pytest.approx(output_cost * 1.5)
+    assert info["cache_read_input_token_cost_above_272k_tokens"] == pytest.approx(cache_read_cost * 2)
+
+    assert info["max_input_tokens"] == 1050000
+    assert info["max_output_tokens"] == 128000
+    assert info["max_tokens"] == 128000
+
+    assert info["supports_function_calling"] is True
+    assert info["supports_prompt_caching"] is True
+    assert info["supports_reasoning"] is True
+    assert info["supports_response_schema"] is True
+    assert info["supports_tool_choice"] is True
+    assert info["supports_vision"] is True
+    assert info["supports_web_search"] is True
+    assert info["supports_none_reasoning_effort"] is True
+    assert info["supports_xhigh_reasoning_effort"] is True
+    assert info["supports_minimal_reasoning_effort"] is False
+
+    assert info["supported_endpoints"] == ["/v1/chat/completions", "/v1/batch", "/v1/responses"]
+    assert info["supported_modalities"] == ["text", "image"]
+    assert info["supported_output_modalities"] == ["text"]
+
+    routed_model, provider, _, _ = get_llm_provider(model=f"openai/{model}")
+    assert routed_model == model
+    assert provider == "openai"
+
+
+def test_gpt_5_6_backup_matches_main():
+    """Ensure the bundled model cost map stays in sync with the canonical file."""
+    repo_root = Path(__file__).parents[2]
+    main_path = repo_root / "model_prices_and_context_window.json"
+    backup_path = repo_root / "litellm" / "model_prices_and_context_window_backup.json"
+
+    with open(main_path) as f:
+        main_cost = json.load(f)
+    with open(backup_path) as f:
+        backup_cost = json.load(f)
+
+    for model in GPT_5_6_MODELS:
+        assert backup_cost.get(model) == main_cost.get(model), (
+            f"{model} differs between main and backup model cost maps"
+        )
diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py
index 053fe970d3e..47be8cb2eec 100644
--- a/tests/test_litellm/test_utils.py
+++ b/tests/test_litellm/test_utils.py
@@ -710,6 +710,9 @@ def test_aaamodel_prices_and_context_window_json_is_valid():
                 "cache_creation_input_token_cost": {"type": "number"},
                 "cache_creation_input_token_cost_above_1hr": {"type": "number"},
                 "cache_creation_input_token_cost_above_200k_tokens": {"type": "number"},
+                "cache_creation_input_token_cost_above_272k_tokens": {"type": "number"},
+                "cache_creation_input_token_cost_flex": {"type": "number"},
+                "cache_creation_input_token_cost_priority": {"type": "number"},
                 "cache_read_input_token_cost": {"type": "number"},
                 "cache_read_input_token_cost_above_200k_tokens": {"type": "number"},
                 "cache_read_input_token_cost_above_272k_tokens": {"type": "number"},

From 7d63b86e00cf0b4acabf5f582eef6a9e904c57e6 Mon Sep 17 00:00:00 2001
From: ryan-crabbe-berri 
Date: Thu, 9 Jul 2026 11:59:16 -0700
Subject: [PATCH 064/399] fix(ui): forward refs through ui primitives and fail
 tests on swallowed refs (#32401)

* fix(ui): forward refs through ui primitives and fail tests on swallowed refs

Under React 18 a ref passed to a plain function component is dropped
with only a dev console warning, so Base UI render-prop triggers
composed over our shadcn-style primitives silently stop working (the
tooltip just never opens; ui/badge.tsx hit exactly this on the shared
DataTable branch). Label, Separator, Skeleton, UiLoadingSpinner and the
Table family now use React.forwardRef like Button and Input already
did, a contract test pins ref delivery for each, and setupTests turns
React's ref warning into a test failure so the next primitive that
swallows a ref fails CI instead of shipping a dead tooltip

* fix(ui): include captured ref warnings in the tripwire error

The afterEach tripwire threw a fixed message and discarded the collected
React warnings, so a failure never said which component swallowed the ref.
Append the captured warnings (component name + stack) to the thrown error.
---
 .../src/components/ui/label.tsx               |  10 +-
 .../src/components/ui/ref-forwarding.test.tsx | 103 ++++++++++++++++++
 .../src/components/ui/separator.tsx           |  11 +-
 .../src/components/ui/skeleton.tsx            |  11 +-
 .../src/components/ui/table.tsx               |  85 +++++++++------
 .../src/components/ui/ui-loading-spinner.tsx  |  68 ++++++------
 ui/litellm-dashboard/tests/setupTests.ts      |  22 ++++
 7 files changed, 235 insertions(+), 75 deletions(-)
 create mode 100644 ui/litellm-dashboard/src/components/ui/ref-forwarding.test.tsx

diff --git a/ui/litellm-dashboard/src/components/ui/label.tsx b/ui/litellm-dashboard/src/components/ui/label.tsx
index ded2dfc1a7b..1ac4eed0d4e 100644
--- a/ui/litellm-dashboard/src/components/ui/label.tsx
+++ b/ui/litellm-dashboard/src/components/ui/label.tsx
@@ -4,9 +4,10 @@ import * as React from "react";
 
 import { cn } from "@/lib/cva.config";
 
-function Label({ className, ...props }: React.ComponentProps<"label">) {
-  return (
+const Label = React.forwardRef>(
+  ({ className, ...props }, ref) => (
     
+ caption + + + h + + + + + d + + + + + f + + +
, + ); + + expect(table.current).toBeInstanceOf(HTMLTableElement); + expect(caption.current).toBeInstanceOf(HTMLTableCaptionElement); + expect(header.current?.tagName).toBe("THEAD"); + expect(body.current?.tagName).toBe("TBODY"); + expect(footer.current?.tagName).toBe("TFOOT"); + expect(row.current).toBeInstanceOf(HTMLTableRowElement); + expect(head.current?.tagName).toBe("TH"); + expect(cell.current?.tagName).toBe("TD"); + }); +}); + +describe("setupTests ref tripwire", () => { + it("records a violation when a ref is passed to a plain function component", () => { + const Plain = (props: React.ComponentPropsWithoutRef<"span">) => ; + const ref = React.createRef(); + render(React.createElement(Plain as never, { ref })); + const consume = (globalThis as { __consumePendingRefWarnings?: () => string[] }).__consumePendingRefWarnings; + expect(consume).toBeDefined(); + const violations = consume!(); + expect(violations).toHaveLength(1); + expect(violations[0]).toContain("Function components cannot be given refs"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/ui/separator.tsx b/ui/litellm-dashboard/src/components/ui/separator.tsx index 443f8e905f9..a8a8d9cf5c2 100644 --- a/ui/litellm-dashboard/src/components/ui/separator.tsx +++ b/ui/litellm-dashboard/src/components/ui/separator.tsx @@ -1,12 +1,14 @@ "use client"; import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"; +import * as React from "react"; import { cn } from "@/lib/cva.config"; -function Separator({ className, orientation = "horizontal", ...props }: SeparatorPrimitive.Props) { - return ( +const Separator = React.forwardRef, SeparatorPrimitive.Props>( + ({ className, orientation = "horizontal", ...props }, ref) => ( - ); -} + ), +); +Separator.displayName = "Separator"; export { Separator }; diff --git a/ui/litellm-dashboard/src/components/ui/skeleton.tsx b/ui/litellm-dashboard/src/components/ui/skeleton.tsx index e27145708a2..69ff4891cec 100644 --- a/ui/litellm-dashboard/src/components/ui/skeleton.tsx +++ b/ui/litellm-dashboard/src/components/ui/skeleton.tsx @@ -1,7 +1,12 @@ +import * as React from "react"; + import { cn } from "@/lib/cva.config"; -function Skeleton({ className, ...props }: React.ComponentProps<"div">) { - return
; -} +const Skeleton = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +Skeleton.displayName = "Skeleton"; export { Skeleton }; diff --git a/ui/litellm-dashboard/src/components/ui/table.tsx b/ui/litellm-dashboard/src/components/ui/table.tsx index aff687f432d..6271a9e89ac 100644 --- a/ui/litellm-dashboard/src/components/ui/table.tsx +++ b/ui/litellm-dashboard/src/components/ui/table.tsx @@ -4,35 +4,45 @@ import * as React from "react"; import { cn } from "@/lib/cva.config"; -function Table({ className, ...props }: React.ComponentProps<"table">) { - return ( +const Table = React.forwardRef>( + ({ className, ...props }, ref) => (
- +
- ); -} + ), +); +Table.displayName = "Table"; -function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { - return ; -} +const TableHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +TableHeader.displayName = "TableHeader"; -function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { - return ; -} +const TableBody = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +TableBody.displayName = "TableBody"; -function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { - return ( +const TableFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( tr]:last:border-b-0", className)} {...props} /> - ); -} + ), +); +TableFooter.displayName = "TableFooter"; -function TableRow({ className, ...props }: React.ComponentProps<"tr">) { - return ( +const TableRow = React.forwardRef>( + ({ className, ...props }, ref) => ( ) { )} {...props} /> - ); -} + ), +); +TableRow.displayName = "TableRow"; -function TableHead({ className, ...props }: React.ComponentProps<"th">) { - return ( +const TableHead = React.forwardRef>( + ({ className, ...props }, ref) => ( + + + )} + />, + ); + + expect(screen.getByTestId("footer-row").closest("tfoot")).not.toBeNull(); + }); +}); + +describe("DataTable layout", () => { + it("exposes resize handles with stable selectors only when resizing is enabled", () => { + const { container, rerender } = render( + , + ); + expect(container.querySelectorAll("[data-resizer][data-header-id]").length).toBe(2); + + rerender(); + expect(container.querySelectorAll("[data-resizer]").length).toBe(0); + }); + + it("makes the header sticky and constrains body height when maxBodyHeight is set", () => { + const { container } = render(); + expect(container.querySelector("thead")?.className).toContain("sticky"); + const scroller = container.querySelector('[data-slot="table-container"]')?.parentElement as HTMLElement; + expect(scroller.style.maxHeight).toBe("240px"); + }); +}); + +describe("DataTable misconfiguration guards", () => { + it("throws when server sorting is missing required props", () => { + const spy = vi.spyOn(console, "error").mockImplementation(() => {}); + expect(() => render()).toThrow( + /sortingMode='server'/, + ); + spy.mockRestore(); + }); + + it("throws when server pagination is missing required props", () => { + const spy = vi.spyOn(console, "error").mockImplementation(() => {}); + expect(() => render()).toThrow( + /paginationMode='server'/, + ); + spy.mockRestore(); + }); + + it("throws when both defaultSorting and sorting are provided", () => { + const spy = vi.spyOn(console, "error").mockImplementation(() => {}); + expect(() => + render( + , + ), + ).toThrow(/defaultSorting/); + spy.mockRestore(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx new file mode 100644 index 00000000000..7655454f381 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx @@ -0,0 +1,497 @@ +"use client"; + +import { + type Cell, + type Column, + type ColumnDef, + type ColumnPinningState, + type ColumnSizingState, + type ExpandedState, + flexRender, + getCoreRowModel, + getExpandedRowModel, + getPaginationRowModel, + getSortedRowModel, + type Header, + type OnChangeFn, + type Row, + type RowData, + type Table, + type TableOptions, + useReactTable, + type VisibilityState, +} from "@tanstack/react-table"; +import * as React from "react"; +import { Fragment, useState } from "react"; + +import { + Table as TableRoot, + TableBody, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { cn } from "@/lib/cva.config"; + +import "./columnMeta"; +import { DataTablePagination } from "./DataTablePagination"; +import type { ColumnPinnedSide, DataTableProps, DataTableSize, PaginationMode, SortingMode } from "./types"; + +const DEFAULT_PAGE_SIZE_OPTIONS = [25, 50, 100]; + +const INTERACTIVE_SELECTOR = "button, a, input, select, textarea, [role=checkbox], [data-row-click-exempt]"; + +const noop = () => {}; + +export class DataTableConfigError extends Error { + constructor(messages: readonly string[]) { + super(`DataTable misconfiguration:\n- ${messages.join("\n- ")}`); + this.name = "DataTableConfigError"; + } +} + +export function validateDataTableConfig( + props: DataTableProps, +): readonly string[] { + const serverSortingIncomplete = + props.sortingMode === "server" && (props.sorting === undefined || props.onSortingChange === undefined); + + const serverPaginationPropsMissing = + props.pagination === undefined || props.onPaginationChange === undefined || props.rowCount === undefined; + const serverPaginationIncomplete = props.paginationMode === "server" && serverPaginationPropsMissing; + + const bothSortingSources = props.defaultSorting !== undefined && props.sorting !== undefined; + + return [ + serverSortingIncomplete ? "sortingMode='server' requires both `sorting` and `onSortingChange`." : null, + serverPaginationIncomplete + ? "paginationMode='server' requires `pagination`, `onPaginationChange`, and `rowCount`." + : null, + bothSortingSources ? "Provide either `defaultSorting` (uncontrolled) or `sorting` (controlled), not both." : null, + ].filter((message): message is string => message !== null); +} + +function columnDefId(column: ColumnDef): string | undefined { + if ("id" in column && typeof column.id === "string") { + return column.id; + } + if ("accessorKey" in column && column.accessorKey != null) { + return String(column.accessorKey); + } + return undefined; +} + +function derivePinning(columns: ColumnDef[]): ColumnPinningState { + const collect = (side: ColumnPinnedSide): string[] => + columns + .filter((column) => column.meta?.pinned === side) + .map(columnDefId) + .filter((id): id is string => id !== undefined); + return { left: collect("left"), right: collect("right") }; +} + +function buildRowModels( + sortingMode: SortingMode, + paginationMode: PaginationMode, + getRowCanExpand: ((row: Row) => boolean) | undefined, +): Partial> { + return { + ...(sortingMode === "client" ? { getSortedRowModel: getSortedRowModel() } : {}), + ...(paginationMode === "client" ? { getPaginationRowModel: getPaginationRowModel() } : {}), + ...(getRowCanExpand !== undefined ? { getRowCanExpand, getExpandedRowModel: getExpandedRowModel() } : {}), + }; +} + +function stickyZIndex(isPinned: boolean, isHeader: boolean): number { + if (isPinned && isHeader) { + return 30; + } + if (isHeader) { + return 20; + } + return 10; +} + +function pinnedShadow(pinned: false | ColumnPinnedSide): string { + if (pinned === "left") { + return "shadow-[inset_-1px_0_0_var(--color-border)]"; + } + if (pinned === "right") { + return "shadow-[inset_1px_0_0_var(--color-border)]"; + } + return ""; +} + +function computeStickyStyle( + column: Column, + isHeader: boolean, + stickyHeader: boolean, +): { style: React.CSSProperties; className: string } { + const pinned = column.getIsPinned(); + const stickyTop = isHeader && stickyHeader; + if (!pinned && !stickyTop) { + return { style: {}, className: "" }; + } + + const left = pinned === "left" ? column.getStart("left") : undefined; + const right = pinned === "right" ? column.getAfter("right") : undefined; + + const style: React.CSSProperties = { + position: "sticky", + zIndex: stickyZIndex(pinned !== false, isHeader), + ...(stickyTop ? { top: 0 } : {}), + ...(left !== undefined ? { left } : {}), + ...(right !== undefined ? { right } : {}), + }; + + return { style, className: cn(pinned ? "bg-background" : "", pinnedShadow(pinned)) }; +} + +function widthStyle( + column: Column, + enableColumnResizing: boolean, +): React.CSSProperties | undefined { + if (enableColumnResizing || column.columnDef.size !== undefined) { + return { width: column.getSize() }; + } + return undefined; +} + +interface HeadCellProps { + header: Header; + size: DataTableSize; + stickyHeader: boolean; + enableColumnResizing: boolean; +} + +function DataTableHeadCell({ header, size, stickyHeader, enableColumnResizing }: HeadCellProps) { + const { column } = header; + const meta = column.columnDef.meta; + const sticky = computeStickyStyle(column, true, stickyHeader); + const canResize = enableColumnResizing && column.getCanResize(); + + return ( + + {header.isPlaceholder ? null : ( +
+ {flexRender(column.columnDef.header, header.getContext())} +
+ )} + {canResize && ( +
column.resetSize()} + className={cn( + "absolute top-0 right-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-border", + column.getIsResizing() ? "bg-primary" : "", + )} + /> + )} + + ); +} + +interface BodyCellProps { + cell: Cell; + size: DataTableSize; + stickyHeader: boolean; + enableColumnResizing: boolean; +} + +function DataTableBodyCell({ cell, size, stickyHeader, enableColumnResizing }: BodyCellProps) { + const { column } = cell; + const meta = column.columnDef.meta; + const sticky = computeStickyStyle(column, false, stickyHeader); + + return ( + + {flexRender(column.columnDef.cell, cell.getContext())} + + ); +} + +interface BodyRowProps { + row: Row; + size: DataTableSize; + stickyHeader: boolean; + enableColumnResizing: boolean; + onRowClick?: (row: TData) => void; + rowClassName?: (row: Row) => string; + renderSubComponent?: (props: { row: Row }) => React.ReactElement; +} + +function DataTableBodyRow({ + row, + size, + stickyHeader, + enableColumnResizing, + onRowClick, + rowClassName, + renderSubComponent, +}: BodyRowProps) { + const clickable = onRowClick !== undefined; + const cells = row.getVisibleCells(); + + const handleClick = (event: React.MouseEvent) => { + if (onRowClick === undefined) { + return; + } + const target = event.target as HTMLElement | null; + if (target === null || !event.currentTarget.contains(target)) { + return; + } + if (target.closest(INTERACTIVE_SELECTOR) !== null) { + return; + } + onRowClick(row.original); + }; + + return ( + + + {cells.map((cell) => ( + + ))} + + {renderSubComponent !== undefined && row.getIsExpanded() && ( + + + {renderSubComponent({ row })} + + + )} + + ); +} + +function MessageRow({ colSpan, children }: { colSpan: number; children: React.ReactNode }) { + return ( + + + {children} + + + ); +} + +function useControllable( + controlled: T | undefined, + controlledOnChange: OnChangeFn | undefined, + initial: T, +): { value: T; onChange: OnChangeFn } { + const [internal, setInternal] = useState(initial); + if (controlled !== undefined) { + return { value: controlled, onChange: controlledOnChange ?? noop }; + } + return { value: internal, onChange: setInternal }; +} + +function useDataTableInstance(props: DataTableProps): Table { + const { + data, + columns, + getRowId, + sortingMode = "none", + sorting, + onSortingChange, + defaultSorting, + enableSortingRemoval = false, + paginationMode = "none", + pagination, + onPaginationChange, + rowCount, + pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, + enableColumnResizing = false, + columnResizeMode = "onEnd", + defaultColumnVisibility, + getRowCanExpand, + renderSubComponent, + expanded, + onExpandedChange, + } = props; + + const sortingState = useControllable(sorting, onSortingChange, defaultSorting ?? []); + const paginationState = useControllable(pagination, onPaginationChange, { + pageIndex: 0, + pageSize: pageSizeOptions[0] ?? 25, + }); + const expandedState = useControllable(expanded, onExpandedChange, {}); + const [columnVisibility, setColumnVisibility] = useState(defaultColumnVisibility ?? {}); + const [columnSizing, setColumnSizing] = useState({}); + const columnPinning = React.useMemo(() => derivePinning(columns), [columns]); + const expansionGuard = renderSubComponent !== undefined ? getRowCanExpand : undefined; + + const tableOptions: TableOptions = { + data, + columns, + state: { + sorting: sortingState.value, + pagination: paginationState.value, + expanded: expandedState.value, + columnVisibility, + columnSizing, + }, + initialState: { columnPinning }, + manualSorting: sortingMode === "server", + manualPagination: paginationMode === "server", + enableSortingRemoval, + enableColumnResizing, + columnResizeMode, + onSortingChange: sortingState.onChange, + onPaginationChange: paginationState.onChange, + onExpandedChange: expandedState.onChange, + onColumnVisibilityChange: setColumnVisibility, + onColumnSizingChange: setColumnSizing, + getCoreRowModel: getCoreRowModel(), + ...buildRowModels(sortingMode, paginationMode, expansionGuard), + ...(getRowId !== undefined ? { getRowId } : {}), + ...(paginationMode === "server" && rowCount !== undefined ? { rowCount } : {}), + }; + + return useReactTable(tableOptions); +} + +export function DataTable(props: DataTableProps) { + // Validate once at construction so a misconfig surfaces immediately instead of on every render. + useState(() => { + const errors = validateDataTableConfig(props); + if (errors.length > 0) { + throw new DataTableConfigError(errors); + } + return null; + }); + + const { + isLoading = false, + loadingMessage = "Loading…", + noDataMessage = "No results", + paginationMode = "none", + rowCount, + pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, + enableColumnResizing = false, + onRowClick, + rowClassName, + renderSubComponent, + maxBodyHeight, + size = "default", + toolbar, + paginationSlot, + footer, + } = props; + + const table = useDataTableInstance(props); + + const rows = table.getRowModel().rows; + const visibleColumnCount = table.getVisibleLeafColumns().length; + const stickyHeader = maxBodyHeight !== undefined; + const tableStyle = enableColumnResizing ? { width: table.getTotalSize() } : undefined; + + const renderPagination = (): React.ReactNode => { + if (paginationSlot !== undefined) { + return paginationSlot(table); + } + if (paginationMode === "none") { + return null; + } + const current = table.getState().pagination; + const total = paginationMode === "server" ? rowCount ?? 0 : table.getPrePaginationRowModel().rows.length; + return ( + table.setPageIndex(next)} + onPageSizeChange={(next) => table.setPageSize(next)} + pageSizeOptions={pageSizeOptions} + isLoading={isLoading} + /> + ); + }; + + const renderBody = (): React.ReactNode => { + if (isLoading) { + return {loadingMessage}; + } + if (rows.length === 0) { + return {noDataMessage}; + } + return rows.map((row) => ( + + )); + }; + + return ( +
+ {toolbar !== undefined &&
{toolbar(table)}
} +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + {renderBody()} + {footer !== undefined && {footer(table)}} + +
+ {renderPagination()} +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.test.tsx new file mode 100644 index 00000000000..e5bd4a55b53 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.test.tsx @@ -0,0 +1,68 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; + +import { DataTablePagination } from "./DataTablePagination"; + +const baseProps = { + page: 0, + pageSize: 25, + rowCount: 100, + onPageChange: () => {}, + onPageSizeChange: () => {}, +}; + +describe("DataTablePagination", () => { + it("renders the current range from plain props", () => { + render(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-25 of 100"); + }); + + it("computes the range for a middle page and clamps the end to rowCount", () => { + render(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 91-100 of 100"); + }); + + it("disables the previous controls on the first page", () => { + render(); + expect(screen.getByTestId("pagination-first")).toBeDisabled(); + expect(screen.getByTestId("pagination-prev")).toBeDisabled(); + expect(screen.getByTestId("pagination-next")).toBeEnabled(); + }); + + it("disables the next controls on the last page", () => { + render(); + expect(screen.getByTestId("pagination-next")).toBeDisabled(); + expect(screen.getByTestId("pagination-last")).toBeDisabled(); + expect(screen.getByTestId("pagination-prev")).toBeEnabled(); + }); + + it("advances by one page when next is clicked", async () => { + const user = userEvent.setup(); + const onPageChange = vi.fn(); + render(); + await user.click(screen.getByTestId("pagination-next")); + expect(onPageChange).toHaveBeenCalledWith(2); + }); + + it("jumps to the last page index when last is clicked", async () => { + const user = userEvent.setup(); + const onPageChange = vi.fn(); + render(); + await user.click(screen.getByTestId("pagination-last")); + expect(onPageChange).toHaveBeenCalledWith(3); + }); + + it("shows an empty state and disables all navigation when there are no rows", () => { + render(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("No results"); + expect(screen.getByTestId("pagination-next")).toBeDisabled(); + expect(screen.getByTestId("pagination-prev")).toBeDisabled(); + }); + + it("disables navigation while loading", () => { + render(); + expect(screen.getByTestId("pagination-next")).toBeDisabled(); + expect(screen.getByTestId("pagination-prev")).toBeDisabled(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.tsx new file mode 100644 index 00000000000..5a30b12f27f --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.tsx @@ -0,0 +1,113 @@ +"use client"; + +import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { cn } from "@/lib/cva.config"; + +export const DEFAULT_PAGE_SIZE_OPTIONS = [25, 50, 100]; + +export interface DataTablePaginationProps { + page: number; + pageSize: number; + rowCount: number; + onPageChange: (page: number) => void; + onPageSizeChange: (pageSize: number) => void; + pageSizeOptions?: number[]; + isLoading?: boolean; + className?: string; +} + +export function DataTablePagination({ + page, + pageSize, + rowCount, + onPageChange, + onPageSizeChange, + pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, + isLoading = false, + className, +}: DataTablePaginationProps) { + const pageCount = pageSize > 0 ? Math.ceil(rowCount / pageSize) : 0; + const start = rowCount === 0 ? 0 : page * pageSize + 1; + const end = Math.min((page + 1) * pageSize, rowCount); + const canPrev = page > 0 && !isLoading; + const canNext = page < pageCount - 1 && !isLoading; + const lastPage = Math.max(pageCount - 1, 0); + + return ( +
+
+ Rows per page + +
+ +
+ + {rowCount === 0 ? "No results" : `Showing ${start}-${end} of ${rowCount}`} + +
+ + + + +
+
+
+ ); +} diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx new file mode 100644 index 00000000000..a6164307a78 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx @@ -0,0 +1,112 @@ +import { + type ColumnDef, + flexRender, + getCoreRowModel, + getSortedRowModel, + type OnChangeFn, + type SortingState, + useReactTable, +} from "@tanstack/react-table"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { useState } from "react"; +import { describe, expect, it, vi } from "vitest"; + +import { DataTableSortHeader, type DataTableSortVariant } from "./DataTableSortHeader"; + +interface Item { + name: string; +} + +interface HarnessProps { + variant: DataTableSortVariant; + canSort?: boolean; + onSortingChange?: OnChangeFn; +} + +function SortHeaderHarness({ variant, canSort = true, onSortingChange }: HarnessProps) { + const [sorting, setSorting] = useState([]); + const columns: ColumnDef[] = [ + { + accessorKey: "name", + enableSorting: canSort, + header: ({ column }) => , + }, + ]; + const options = { + data: [{ name: "x" }], + columns, + state: { sorting }, + onSortingChange: (updater: SortingState | ((prev: SortingState) => SortingState)) => { + setSorting(updater); + onSortingChange?.(updater); + }, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + }; + const table = useReactTable(options); + + return ( +
[role=checkbox]]:translate-y-[2px]", @@ -53,12 +65,14 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) { )} {...props} /> - ); -} + ), +); +TableHead.displayName = "TableHead"; -function TableCell({ className, ...props }: React.ComponentProps<"td">) { - return ( +const TableCell = React.forwardRef>( + ({ className, ...props }, ref) => ( [role=checkbox]]:translate-y-[2px]", @@ -66,13 +80,20 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) { )} {...props} /> - ); -} + ), +); +TableCell.displayName = "TableCell"; -function TableCaption({ className, ...props }: React.ComponentProps<"caption">) { - return ( -
- ); -} +const TableCaption = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +TableCaption.displayName = "TableCaption"; export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }; diff --git a/ui/litellm-dashboard/src/components/ui/ui-loading-spinner.tsx b/ui/litellm-dashboard/src/components/ui/ui-loading-spinner.tsx index 09e52ef0d48..5fd62d92973 100644 --- a/ui/litellm-dashboard/src/components/ui/ui-loading-spinner.tsx +++ b/ui/litellm-dashboard/src/components/ui/ui-loading-spinner.tsx @@ -4,39 +4,43 @@ import { cx } from "@/lib/cva.config"; type LoadingSpinnerProps = React.SVGProps; -export function UiLoadingSpinner({ className = "", ...props }: LoadingSpinnerProps) { - const id = useId(); +export const UiLoadingSpinner = React.forwardRef( + ({ className = "", ...props }, ref) => { + const id = useId(); - useSafeLayoutEffect(() => { - const animations = document - .getAnimations() - .filter((a) => a instanceof CSSAnimation && a.animationName === "spin") as CSSAnimation[]; + useSafeLayoutEffect(() => { + const animations = document + .getAnimations() + .filter((a) => a instanceof CSSAnimation && a.animationName === "spin") as CSSAnimation[]; - const self = animations.find((a) => (a.effect as KeyframeEffect).target?.getAttribute("data-spinner-id") === id); + const self = animations.find((a) => (a.effect as KeyframeEffect).target?.getAttribute("data-spinner-id") === id); - const anyOther = animations.find( - (a) => a.effect instanceof KeyframeEffect && a.effect.target?.getAttribute("data-spinner-id") !== id, + const anyOther = animations.find( + (a) => a.effect instanceof KeyframeEffect && a.effect.target?.getAttribute("data-spinner-id") !== id, + ); + + if (self && anyOther) { + self.currentTime = anyOther.currentTime; + } + }, [id]); + + return ( + + + + ); - - if (self && anyOther) { - self.currentTime = anyOther.currentTime; - } - }, [id]); - - return ( - - - - - ); -} + }, +); +UiLoadingSpinner.displayName = "UiLoadingSpinner"; diff --git a/ui/litellm-dashboard/tests/setupTests.ts b/ui/litellm-dashboard/tests/setupTests.ts index 6bc2e7775a1..69506a9f246 100644 --- a/ui/litellm-dashboard/tests/setupTests.ts +++ b/ui/litellm-dashboard/tests/setupTests.ts @@ -149,8 +149,30 @@ vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ }), })); +const pendingRefWarnings: string[] = []; +const consumePendingRefWarnings = (): string[] => pendingRefWarnings.splice(0, pendingRefWarnings.length); +(globalThis as { __consumePendingRefWarnings?: () => string[] }).__consumePendingRefWarnings = + consumePendingRefWarnings; + +const originalConsoleError = console.error.bind(console); +vi.spyOn(console, "error").mockImplementation((...args: unknown[]) => { + originalConsoleError(...args); + if (typeof args[0] === "string" && args[0].includes("Function components cannot be given refs")) { + pendingRefWarnings.push(args.map(String).join(" ")); + } +}); + afterEach(() => { cleanup(); + const refWarnings = consumePendingRefWarnings(); + if (refWarnings.length > 0) { + throw new Error( + "A ref was passed to a plain function component and silently dropped under React 18, which breaks " + + "ref-based composition (Base UI render triggers, tooltips, focus). Wrap the component in React.forwardRef. " + + "This tripwire lives in tests/setupTests.ts and can be removed after the React 19 upgrade.\n\n" + + refWarnings.join("\n\n"), + ); + } }); // Make toLocaleString deterministic in tests; individual tests can override From 1d9a86eac40fca902673ba57c613d6d9a6febe37 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Thu, 9 Jul 2026 11:59:22 -0700 Subject: [PATCH 065/399] refactor(ui): consolidate invitation flow into the dashboard layout (#32576) The App Router migration is complete: every page is a path route and the legacy `?page=` switch is gone from the index. This closes it out. The `/ui/` index (page.tsx) kept its own duplicate copy of teams state, a teams fetch, and keys/addKey plumbing solely to feed a second `UserDashboard` render for the `invitation_id` case. That was redundant: `ApiKeysDashboard` already renders `UserDashboard` sourcing its own data, so the index is thinned to just render ``. The login redirect, the legacy `?page=` deep-link redirect for old bookmarks, and the post-login return-URL handling stay on the index. The invitation entry point now resolves in one place. Modern invitation links already point at the dedicated `/onboarding` route; the dashboard layout now redirects legacy `/ui/?invitation_id=` links there too (via `migratedHref`, the same base-aware redirect the index uses for `?page=`), instead of re-rendering that route's page component inline. This removes an import of one route's `page.tsx` into another module, and lets the now-unreachable `if (invitation_id) return ` branch in the shared `user_dashboard.tsx` be deleted along with its dead `Onboarding` import and `searchParams` read. A layout test asserts the redirect and fails if it regresses. `legacyPageHref` and the sidebar's migrated-vs-legacy href fallback are left in place; they are still live for the parent-category nav nodes (agentic, tools, experimental, settings) that are not page routes. eslint-metrics.json is resynced: -2 no-explicit-any from the removed `any` casts, plus pre-existing drift the gate requires the snapshot to match. --- ui/litellm-dashboard/eslint-metrics.json | 2 +- .../src/app/(dashboard)/layout.test.tsx | 30 ++++++++- .../src/app/(dashboard)/layout.tsx | 13 +++- .../src/app/(dashboard)/page.tsx | 62 +++---------------- .../src/components/user_dashboard.tsx | 11 ---- 5 files changed, 47 insertions(+), 71 deletions(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index 37bad071081..fcf60934f64 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,5 +1,5 @@ { - "@typescript-eslint/no-explicit-any": 1982, + "@typescript-eslint/no-explicit-any": 1980, "complexity": 128, "local/no-large-inline-object-arg": 519, "local/no-long-condition-chain": 233, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx index 7573ddb5a0f..92a1d40b0e3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx @@ -3,9 +3,13 @@ import { render, screen, waitFor } from "@testing-library/react"; import { AuthProvider } from "@/contexts/AuthContext"; import Layout from "./layout"; +const { replaceMock } = vi.hoisted(() => ({ replaceMock: vi.fn() })); + +let searchParamsValue = new URLSearchParams(); + vi.mock("next/navigation", () => ({ - useRouter: vi.fn(() => ({ push: vi.fn(), replace: vi.fn() })), - useSearchParams: vi.fn(() => new URLSearchParams()), + useRouter: vi.fn(() => ({ push: vi.fn(), replace: replaceMock })), + useSearchParams: vi.fn(() => searchParamsValue), usePathname: vi.fn(() => "/ui/guardrails"), })); @@ -58,6 +62,7 @@ describe("(dashboard) Layout", () => { beforeEach(() => { vi.clearAllMocks(); pendingUiConfig = createDeferred(); + searchParamsValue = new URLSearchParams(); }); it("does not mount route content until getUiConfig has resolved", async () => { @@ -79,4 +84,25 @@ describe("(dashboard) Layout", () => { expect(screen.getByTestId("navbar")).toBeTruthy(); expect(screen.queryByTestId("loading-screen")).toBeNull(); }); + + it("redirects an invitation link to the onboarding route instead of rendering the dashboard shell", async () => { + searchParamsValue = new URLSearchParams("invitation_id=abc123"); + + render( + + +
+ + , + ); + + pendingUiConfig.resolve(); + + await waitFor(() => + expect(replaceMock).toHaveBeenCalledWith(expect.stringContaining("/onboarding?invitation_id=abc123")), + ); + expect(screen.queryByTestId("page-content")).toBeNull(); + expect(screen.queryByTestId("navbar")).toBeNull(); + expect(screen.queryByTestId("sidebar")).toBeNull(); + }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx index c84209b80cf..b8eb4e66ed0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx @@ -137,17 +137,26 @@ function DashboardShell({ children }: { children: React.ReactNode }) { } function LayoutContent({ children }: { children: React.ReactNode }) { + const router = useRouter(); const searchParams = useSearchParams(); const { accessToken, authLoading } = useAuth(); const isInvitationFlow = Boolean(searchParams.get("invitation_id")); - if (authLoading) { + // Legacy invitation links point at /ui/?invitation_id=; the onboarding form now lives at its own + // /onboarding route. Redirect once ui-config has loaded so migratedHref resolves the SERVER_ROOT_PATH base. + useEffect(() => { + if (!authLoading && isInvitationFlow) { + router.replace(`${migratedHref("onboarding")}?${searchParams.toString()}`); + } + }, [authLoading, isInvitationFlow, router, searchParams]); + + if (authLoading || isInvitationFlow) { return ; } return ( - {isInvitationFlow ? children : {children}} + {children} ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/page.tsx index cb4a4a0de03..6c0d780183a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/page.tsx @@ -1,11 +1,8 @@ "use client"; import ApiKeysDashboard from "@/app/(dashboard)/api-keys/ApiKeysDashboard"; -import { teamListCall as v2TeamListCall } from "@/app/(dashboard)/hooks/teams/useTeams"; import LoadingScreen from "@/components/common_components/LoadingScreen"; -import { Team } from "@/components/key_team_helpers/key_list"; import { proxyBaseUrl } from "@/components/networking"; -import UserDashboard from "@/components/user_dashboard"; import { useAuth } from "@/contexts/AuthContext"; import { buildLoginUrlWithReturn, @@ -16,32 +13,20 @@ import { } from "@/utils/returnUrlUtils"; import { MIGRATED_PAGES, migratedHref } from "@/utils/migratedPages"; import { useRouter, useSearchParams } from "next/navigation"; -import { Suspense, useEffect, useRef, useState } from "react"; +import { Suspense, useEffect, useRef } from "react"; function CreateKeyPageContent() { - const { authLoading, token, userID, userRole, userEmail, accessToken, premiumUser, setUserRole, setUserEmail } = - useAuth(); - - const [teams, setTeams] = useState(null); - const [keys, setKeys] = useState([]); + const { authLoading, token } = useAuth(); const router = useRouter(); const searchParams = useSearchParams()!; - const [createClicked, setCreateClicked] = useState(false); - - const invitation_id = searchParams.get("invitation_id"); const explicitPage = searchParams.get("page"); - const page = explicitPage || "api-keys"; // Track if we've already attempted a return URL redirect to prevent race conditions const hasAttemptedReturnRedirectRef = useRef(false); - const addKey = (data: any) => { - setKeys((prevData) => (prevData ? [...prevData, data] : [data])); - setCreateClicked(() => !createClicked); - }; - const redirectToLogin = authLoading === false && token === null && invitation_id === null; + const redirectToLogin = authLoading === false && token === null; useEffect(() => { if (redirectToLogin) { @@ -55,15 +40,13 @@ function CreateKeyPageContent() { } }, [redirectToLogin]); - // Redirect legacy query-param pages to their new path-based routes. Only when the page is - // explicitly requested via ?page=, so the bare landing renders inline and the post-login - // return-URL handling below stays intact. + // Redirect legacy ?page= deep links (old bookmarks) to their path-based routes. const isLegacyRedirect = explicitPage !== null && explicitPage in MIGRATED_PAGES; useEffect(() => { if (!authLoading && isLegacyRedirect) { - router.replace(migratedHref(MIGRATED_PAGES[page])); + router.replace(migratedHref(MIGRATED_PAGES[explicitPage])); } - }, [authLoading, isLegacyRedirect, page, router]); + }, [authLoading, isLegacyRedirect, explicitPage, router]); // Check for a stored return URL after successful authentication // This handles the case where user comes back from SSO and we need to redirect to the original URL @@ -102,42 +85,11 @@ function CreateKeyPageContent() { } }, [token]); - useEffect(() => { - if (accessToken && userID && userRole) { - v2TeamListCall(accessToken, 1, 100, { - userID: userRole !== "Admin" && userRole !== "Admin Viewer" ? userID : null, - }) - .then((response) => setTeams(response.teams ?? [])) - .catch(console.error); - } - }, [accessToken, userID, userRole]); - if (authLoading || redirectToLogin || isLegacyRedirect) { return ; } - return ( - <> - {invitation_id ? ( - - ) : ( - - )} - - ); + return ; } export default function CreateKeyPage() { diff --git a/ui/litellm-dashboard/src/components/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index acb9333b051..689b5680fcc 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -2,9 +2,7 @@ import { clearTokenCookies, getCookie } from "@/utils/cookieUtils"; import { Col, Grid } from "@tremor/react"; import { jwtDecode } from "jwt-decode"; -import { useSearchParams } from "next/navigation"; import React, { useEffect, useState } from "react"; -import Onboarding from "../app/onboarding/page"; import { fetchTeams } from "./common_components/fetch_teams"; import { KeyResponse, Team } from "./key_team_helpers/key_list"; import { @@ -76,13 +74,8 @@ const UserDashboard: React.FC = ({ const [userSpendData, setUserSpendData] = useState(null); const [currentOrg, setCurrentOrg] = useState(null); - // Assuming useSearchParams() hook exists and works in your setup - const searchParams = useSearchParams()!; - const token = getCookie("token"); - const invitation_id = searchParams.get("invitation_id"); - const [accessToken, setAccessToken] = useState(null); const [teamSpend, setTeamSpend] = useState(null); const [userModels, setUserModels] = useState([]); @@ -232,10 +225,6 @@ const UserDashboard: React.FC = ({ } }, [selectedTeam]); - if (invitation_id != null) { - return ; - } - function gotoLogin() { // Clear token cookies using the utility function clearTokenCookies(); From 6eed38bcfb4e1c95fb250524117fa29e31da2dfe Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Thu, 9 Jul 2026 13:18:51 -0700 Subject: [PATCH 066/399] fix(guardrails/bedrock): honor disable_exception_on_block by raising ModifyResponseException (#32289) * fix(guardrails/bedrock): honor disable_exception_on_block by raising ModifyResponseException The Bedrock-specific GuardrailInterventionNormalStringError predates the unified guardrails refactor and no proxy code path handles it, so a block with the flag set surfaced as an uncaught Exception -> HTTP 500 in pre_call mode and was silently discarded in during_call mode (model call proceeded in the parallel asyncio.gather; the block hook's data["mock_response"] mutation happened after route_request had already unpacked kwargs). Convert the block to ModifyResponseException at the raise site inside make_bedrock_api_request. That exception is the industry-standard proxy contract already caught in proxy_server, anthropic_endpoints, response_api _endpoints, and pass_through_endpoints; it turns into a 200 response with finish_reason=content_filter and the block message as content, which is exactly what the flag was documented to yield. Post-call blocks attach the LLM response to original_response so the synthetic reply reports the upstream call's real token usage instead of zero. Deletes the now-orphaned GuardrailInterventionNormalStringError class and the dead create_guardrail_blocked_response / mock_response plumbing in the Bedrock hooks; updates the existing tests that had locked in the buggy contract. Resolves LIT-4186 * chore(guardrails/bedrock): drop dead str branch in _update_messages_with_updated_bedrock_guardrail_response Follow-up to the disable_exception_on_block fix. That method used to receive either a BedrockGuardrailResponse or a plain string (the block message, when the flag was set). Now that a block always raises ModifyResponseException before this method runs, the string branch is unreachable; tighten the type to BedrockGuardrailResponse and delete the guard. * fix(guardrails/bedrock): streaming post_call block yields synthetic stream instead of surfacing as SSE 500 Regression from the LIT-4186 refactor: pre-refactor, the streaming post_call iterator caught GuardrailInterventionNormalStringError locally and replaced the assembled response with a synthetic content-filter message, then re-emitted it as chunks via MockResponseIterator. After the refactor the exception was re-raised as ModifyResponseException, which async_streaming_data_generator serializes as a proxy 500 error frame because the SSE response headers are already flushed by the time the block fires. Non-streaming paths still let ModifyResponseException propagate to the endpoint handler (which converts it into a 200). Streaming can't do that, so keep the local synthesis: on the exception, rebind the assembled response to a ModelResponse whose single choice carries the block message as content and finish_reason=content_filter, and let the downstream MockResponseIterator emit it as chunks. Same shape a non-streaming block produces. Adds a mapped-file regression test that mutation-kills the raise behavior and locks in the synthetic-stream contract. * fix(guardrails/bedrock): preserve upstream usage on streaming post_call block Non-streaming post_call blocks report the upstream LLM call's real token usage via ModifyResponseException.original_response, which the endpoint handler unwraps through _blocked_response_usage. Streaming post_call synthesizes its own ModelResponse locally (the exception can't escape the SSE generator), and previously left .usage unset, so the client saw accurate billing on non-streaming blocks and zero on streaming blocks -- silent revenue leak. Copy the assembled response's .usage onto the synthetic block response before yielding. Pre-refactor code had the same gap (create_guardrail_blocked_response never set usage); this is a net improvement, not a regression fix. --- litellm/exceptions.py | 14 - .../guardrail_hooks/bedrock_guardrails.py | 145 ++++--- .../test_bedrock_apply_guardrail.py | 22 +- .../test_bedrock_guardrails.py | 66 ++- .../test_bedrock_guardrails.py | 405 ++++++++++++++++++ 5 files changed, 529 insertions(+), 123 deletions(-) diff --git a/litellm/exceptions.py b/litellm/exceptions.py index adf7b3ef05a..aca3fb551cc 100644 --- a/litellm/exceptions.py +++ b/litellm/exceptions.py @@ -1180,20 +1180,6 @@ class ModifyResponseException(Exception): super().__init__(message) -class GuardrailInterventionNormalStringError( - Exception -): # custom exception to raise when a guardrail intervenes, but we want to return a normal string to the user - def __init__(self, message: str): - self.message = message - super().__init__(self.message) - - def __str__(self): - return self.message - - def __repr__(self): - return self.__str__() - - class SensitiveDataRouteException(Exception): """ Exception raised when a guardrail detects sensitive data and wants to reroute the request. diff --git a/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py b/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py index 6e46f971dd8..a45719d2eb8 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py +++ b/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py @@ -33,7 +33,7 @@ import litellm from litellm._logging import verbose_proxy_logger from litellm.litellm_core_utils.core_helpers import redact_nested_match_and_regex_keys from litellm.caching import DualCache -from litellm.exceptions import GuardrailInterventionNormalStringError +from litellm.exceptions import ModifyResponseException from litellm.integrations.custom_guardrail import CustomGuardrail from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM from litellm.llms.custom_httpx.http_handler import ( @@ -754,7 +754,9 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ) bedrock_guardrail_response = BedrockGuardrailResponse(**_json_response) if self._should_raise_guardrail_blocked_exception(bedrock_guardrail_response): - raise self._get_http_exception_for_blocked_guardrail(bedrock_guardrail_response) + raise self._get_http_exception_for_blocked_guardrail( + bedrock_guardrail_response, request_data=request_data + ) else: status_code, detail_message = self._parse_bedrock_guardrail_error_response(httpx_response) verbose_proxy_logger.error( @@ -1027,8 +1029,8 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): return blocked def _get_http_exception_for_blocked_guardrail( - self, response: BedrockGuardrailResponse - ) -> Union[HTTPException, GuardrailInterventionNormalStringError]: + self, response: BedrockGuardrailResponse, request_data: Optional[dict] = None + ) -> Union[HTTPException, ModifyResponseException]: """ Get the HTTP exception for a blocked guardrail. """ @@ -1040,7 +1042,13 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): bedrock_guardrail_output_text += output.get("text") or "" if self.disable_exception_on_block is True: - return GuardrailInterventionNormalStringError(message=bedrock_guardrail_output_text) + _request_data = request_data or {} + return ModifyResponseException( + message=bedrock_guardrail_output_text, + model=_request_data.get("model", "bedrock-guardrail"), + request_data=_request_data, + guardrail_name=self.guardrail_name, + ) detail: Dict[str, Any] = { "error": "Violated guardrail policy", @@ -1134,18 +1142,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): # This means all actions were ANONYMIZED or NONE, so don't raise exception return False - def create_guardrail_blocked_response(self, response: str) -> ModelResponse: - from litellm.types.utils import Choices, Message, ModelResponse - - return ModelResponse( - choices=[ - Choices( - message=Message(content=response), - ) - ], - model="bedrock-guardrail", - ) - async def async_pre_call_hook( self, user_api_key_dict: UserAPIKeyAuth, @@ -1183,16 +1179,15 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ######################################################### ########## 1. Make the Bedrock API request ########## ######################################################### - bedrock_guardrail_response: Optional[Union[BedrockGuardrailResponse, str]] = None - try: - bedrock_guardrail_response = await self.make_bedrock_api_request( - source="INPUT", - messages=filtered_messages, - request_data=data, - logging_event_type=GuardrailEventHooks.pre_call, - ) - except GuardrailInterventionNormalStringError as e: - bedrock_guardrail_response = e.message + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request; that propagates to the endpoint handler, + # which returns a 200 whose message is the guardrail's blockedInputMessaging. + bedrock_guardrail_response = await self.make_bedrock_api_request( + source="INPUT", + messages=filtered_messages, + request_data=data, + logging_event_type=GuardrailEventHooks.pre_call, + ) ######################################################### ######################################################### @@ -1207,8 +1202,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): updated_target_messages=updated_subset, target_indices=filter_result.target_indices, ) - if isinstance(bedrock_guardrail_response, str): - data["mock_response"] = self.create_guardrail_blocked_response(response=bedrock_guardrail_response) ######################################################### ########## 3. Add the guardrail to the applied guardrails header ########## @@ -1248,16 +1241,19 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ######################################################### ########## 1. Make the Bedrock API request ########## ######################################################### - bedrock_guardrail_response: Optional[Union[BedrockGuardrailResponse, str]] = None - try: - bedrock_guardrail_response = await self.make_bedrock_api_request( - source="INPUT", - messages=filtered_messages, - request_data=data, - logging_event_type=GuardrailEventHooks.during_call, - ) - except GuardrailInterventionNormalStringError as e: - bedrock_guardrail_response = e.message + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request. Because during_call runs in an asyncio.gather + # alongside the LLM call (common_request_processing.py), swallowing the + # exception here to set data["mock_response"] was ineffective: route_request + # unpacked kwargs before this hook ran, and the LLM task's response was taken + # unconditionally. Letting the exception propagate cancels the LLM task and + # the endpoint handler returns the block response. + bedrock_guardrail_response = await self.make_bedrock_api_request( + source="INPUT", + messages=filtered_messages, + request_data=data, + logging_event_type=GuardrailEventHooks.during_call, + ) ######################################################### ######################################################### @@ -1272,8 +1268,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): updated_target_messages=updated_subset, target_indices=filter_result.target_indices, ) - if isinstance(bedrock_guardrail_response, str): - data["mock_response"] = self.create_guardrail_blocked_response(response=bedrock_guardrail_response) ######################################################### ########## 3. Add the guardrail to the applied guardrails header ########## @@ -1323,7 +1317,11 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): # users should configure if they want input validation. Running an # extra INPUT scan here produced a duplicate post-call entry in the # trace and made no semantic sense for a "post-call" event. - output_content_bedrock: Optional[Union[BedrockGuardrailResponse, str]] = None + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request; that propagates to the endpoint handler, + # which returns a 200 whose message is the guardrail's blockedInputMessaging. + # Attach the LLM response to original_response so the synthetic block reply + # reports the real token usage the upstream call consumed instead of zero. try: output_content_bedrock = await self.make_bedrock_api_request( source="OUTPUT", @@ -1332,15 +1330,15 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): request_data=data, logging_event_type=GuardrailEventHooks.post_call, ) - except GuardrailInterventionNormalStringError as e: - output_content_bedrock = e.message + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = response + raise ######################################################### ########## 2. Apply masking to response with output guardrail response ########## ######################################################### - if isinstance(output_content_bedrock, str): - response = self.create_guardrail_blocked_response(response=output_content_bedrock) - elif output_content_bedrock is not None: + if output_content_bedrock is not None: self._apply_masking_to_response( response=response, bedrock_guardrail_response=output_content_bedrock, @@ -1357,7 +1355,7 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): def _update_messages_with_updated_bedrock_guardrail_response( self, messages: List[AllMessageValues], - bedrock_guardrail_response: Union[BedrockGuardrailResponse, str], + bedrock_guardrail_response: BedrockGuardrailResponse, ) -> List[AllMessageValues]: """ Use the output from the bedrock guardrail to mask sensitive content in messages. @@ -1369,8 +1367,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): Returns: List of messages with content masked according to guardrail response """ - if isinstance(bedrock_guardrail_response, str): - return messages # Get masked texts from guardrail response masked_texts = self._extract_masked_texts_from_response(bedrock_guardrail_response) @@ -1422,7 +1418,14 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): # pre_call / during_call. Bedrock will raise if the response # violates the guardrail policy. ################################################################### - output_guardrail_response: Optional[Union[BedrockGuardrailResponse, str]] = None + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request. Non-streaming paths let it propagate so + # the endpoint handler turns it into a 200. Streaming can't do that: the + # SSE response headers are already flushed, so a raise would be serialized + # as an error frame by async_streaming_data_generator. Instead, replace + # the assembled response with the synthetic block content in-place and + # yield it as a normal stream, matching the shape a non-streaming block + # produces. try: output_guardrail_response = await self.make_bedrock_api_request( source="OUTPUT", @@ -1431,15 +1434,31 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): request_data=request_data, logging_event_type=GuardrailEventHooks.post_call, ) - except GuardrailInterventionNormalStringError as e: - output_guardrail_response = e.message + except ModifyResponseException as e: + # Preserve upstream usage from the LLM call we already + # consumed. Non-streaming blocks carry it via + # ModifyResponseException.original_response + + # _blocked_response_usage; streaming has to do the copy + # itself since the exception can't escape this generator. + _original_usage = getattr(assembled_model_response, "usage", None) + assembled_model_response = ModelResponse( + choices=[ + Choices( + index=0, + message=Message(role="assistant", content=e.message), + finish_reason="content_filter", + ) + ], + model=e.model, + ) + if _original_usage is not None: + assembled_model_response.usage = _original_usage + output_guardrail_response = None ######################################################################### ########## 2. Apply masking to response with output guardrail response ########## ######################################################################### - if isinstance(output_guardrail_response, str): - assembled_model_response = self.create_guardrail_blocked_response(response=output_guardrail_response) - elif output_guardrail_response is not None: + if output_guardrail_response is not None: self._apply_masking_to_response( response=assembled_model_response, bedrock_guardrail_response=output_guardrail_response, @@ -1732,13 +1751,13 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): inputs["texts"] = masked_texts return inputs - except (HTTPException, GuardrailInterventionNormalStringError): - # Let guardrail blocking exceptions propagate as-is so the proxy - # can return the correct HTTP status (400) or handle the - # GuardrailInterventionNormalStringError for disable_exception_on_block mode. - # Without this, the generic except below wraps them into a plain - # Exception, losing the HTTP semantics and preventing the proxy - # from properly blocking the call. + except (HTTPException, ModifyResponseException): + # Let guardrail blocking exceptions propagate as-is so the proxy can + # return the correct HTTP status (400 for HTTPException, 200 with the + # block message for ModifyResponseException in disable_exception_on_block + # mode). Without this, the generic except below wraps them into a plain + # Exception, losing the semantics and preventing the proxy from + # properly blocking the call. raise except Exception as e: verbose_proxy_logger.error("Bedrock Guardrail: Failed to apply guardrail: %s", str(e)) diff --git a/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py b/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py index 87c5e3bf2a9..f257b47404e 100644 --- a/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py +++ b/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py @@ -329,12 +329,13 @@ def test_bedrock_guardrail_filters_latest_user_message_when_enabled(): @pytest.mark.asyncio async def test_bedrock_apply_guardrail_blocked_with_disable_exception_on_block(): """ - Regression test for issue #20045: when disable_exception_on_block=True, - make_bedrock_api_request raises GuardrailInterventionNormalStringError. - apply_guardrail must let it propagate as-is so the proxy can handle it - properly instead of wrapping it in a generic Exception. + Regression test for LIT-4186: when disable_exception_on_block=True, a + Bedrock block raises ModifyResponseException. apply_guardrail must let it + propagate as-is so the endpoint handler (proxy_server.py) can turn it into + a 200 response with the block message as content, instead of the exception + surfacing as a bare 500. """ - from litellm.exceptions import GuardrailInterventionNormalStringError + from litellm.exceptions import ModifyResponseException guardrail = BedrockGuardrail( guardrail_name="test-bedrock-guard", @@ -346,18 +347,21 @@ async def test_bedrock_apply_guardrail_blocked_with_disable_exception_on_block() with patch.object( guardrail, "make_bedrock_api_request", new_callable=AsyncMock ) as mock_api: - mock_api.side_effect = GuardrailInterventionNormalStringError( - message="Sorry, your question in its current format is unable to be answered." + mock_api.side_effect = ModifyResponseException( + message="Sorry, your question in its current format is unable to be answered.", + model="bedrock-guardrail", + request_data={}, + guardrail_name="test-bedrock-guard", ) - with pytest.raises(GuardrailInterventionNormalStringError) as exc_info: + with pytest.raises(ModifyResponseException) as exc_info: await guardrail.apply_guardrail( inputs={"texts": ["harmful prompt content"]}, request_data={}, input_type="request", ) - assert "unable to be answered" in str(exc_info.value.message) + assert "unable to be answered" in exc_info.value.message @pytest.mark.asyncio diff --git a/tests/guardrails_tests/test_bedrock_guardrails.py b/tests/guardrails_tests/test_bedrock_guardrails.py index a23e89e576c..823ee05839f 100644 --- a/tests/guardrails_tests/test_bedrock_guardrails.py +++ b/tests/guardrails_tests/test_bedrock_guardrails.py @@ -1390,7 +1390,14 @@ async def test_bedrock_guardrail_disable_exception_on_block_non_streaming(): assert exception.status_code == 400 assert "Violated guardrail policy" in str(exception.detail) - # Test 2: disable_exception_on_block=True - should NOT raise exception + # Test 2: disable_exception_on_block=True - raises ModifyResponseException. + # LIT-4186: pre-fix, the native hook swallowed the block and set + # data["mock_response"], which was dead code (route_request already + # unpacked kwargs) so during_call let the model call proceed anyway. + # The correct contract is to raise ModifyResponseException so the endpoint + # handler returns a 200 with the block message as content. + from litellm.exceptions import ModifyResponseException + guardrail_disabled = BedrockGuardrail( guardrailIdentifier="test-guardrail", guardrailVersion="DRAFT", @@ -1402,20 +1409,13 @@ async def test_bedrock_guardrail_disable_exception_on_block_non_streaming(): ) as mock_post: mock_post.return_value = mock_bedrock_response - # Should NOT raise exception when disable_exception_on_block=True - try: - response = await guardrail_disabled.async_moderation_hook( + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail_disabled.async_moderation_hook( data=request_data, user_api_key_dict=mock_user_api_key_dict, call_type="completion", ) - # Should succeed and return data (even though content was blocked) - assert response is not None - print("✅ No exception raised when disable_exception_on_block=True") - except Exception as e: - pytest.fail( - f"Should not raise exception when disable_exception_on_block=True, but got: {e}" - ) + assert exc_info.value.message == "I can't provide that information." @pytest.mark.asyncio @@ -1514,7 +1514,10 @@ async def test_bedrock_guardrail_disable_exception_on_block_streaming(): async for chunk in result_generator: pass - # Test 2: disable_exception_on_block=True - should NOT raise exception + # Test 2: disable_exception_on_block=True. Streaming can't raise up to the + # endpoint handler (SSE headers already flushed), so the block is delivered + # as a synthetic stream with finish_reason=content_filter and the block + # message as content -- same shape a non-streaming block produces. guardrail_disabled = BedrockGuardrail( guardrailIdentifier="test-guardrail", guardrailVersion="DRAFT", @@ -1526,31 +1529,20 @@ async def test_bedrock_guardrail_disable_exception_on_block_streaming(): ) as mock_post: mock_post.return_value = mock_bedrock_response - # Should NOT raise exception when disable_exception_on_block=True - try: - result_generator = ( - guardrail_disabled.async_post_call_streaming_iterator_hook( - user_api_key_dict=mock_user_api_key_dict, - response=mock_streaming_response(), - request_data=request_data, - ) - ) - - # Consume the generator - should succeed without exceptions - result_chunks = [] - async for chunk in result_generator: - result_chunks.append(chunk) - - # Should have received chunks back even though content was blocked - assert len(result_chunks) > 0 - print( - "✅ Streaming completed without exception when disable_exception_on_block=True" - ) - - except Exception as e: - pytest.fail( - f"Should not raise exception when disable_exception_on_block=True in streaming, but got: {e}" - ) + result_generator = guardrail_disabled.async_post_call_streaming_iterator_hook( + user_api_key_dict=mock_user_api_key_dict, + response=mock_streaming_response(), + request_data=request_data, + ) + chunks = [c async for c in result_generator] + assert chunks, "streaming block should yield synthetic chunks, not empty" + assembled_content = "".join( + (c.choices[0].delta.content or "") + for c in chunks + if getattr(c, "choices", None) and getattr(c.choices[0], "delta", None) + ) + assert assembled_content == "I can't provide that information." + assert chunks[-1].choices[0].finish_reason == "content_filter" @pytest.mark.asyncio diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py index f43d8e85aca..bf237d8017b 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py @@ -2767,3 +2767,408 @@ async def test_grounding_output_blocked_raises_400(): ) assert exc_info.value.status_code == 400 + + +############################################################################### +# LIT-4186: disable_exception_on_block regression tests +# +# Before the fix, a Bedrock block with disable_exception_on_block=True raised +# GuardrailInterventionNormalStringError, which no proxy code handled: the +# unified pre_call path re-raised it, so the client saw HTTP 500 with the block +# message; the native during_call hook swallowed it and set data["mock_response"], +# which was dead code because route_request already unpacked kwargs. +# +# The fix converts blocks to ModifyResponseException at the raise site inside +# make_bedrock_api_request. That exception is already the industry-standard +# proxy contract (caught in proxy_server.py, anthropic_endpoints, etc.) and +# turns into a 200 response whose content is the block message. +############################################################################### + + +def _blocked_bedrock_httpx_response() -> MagicMock: + response = MagicMock() + response.status_code = 200 + response.json.return_value = { + "action": "GUARDRAIL_INTERVENED", + "outputs": [{"text": "Sorry, the model cannot answer this question."}], + "assessments": [ + { + "topicPolicy": { + "topics": [{"name": "Denied", "type": "DENY", "action": "BLOCKED"}] + } + } + ], + } + return response + + +@pytest.mark.asyncio +async def test_make_bedrock_api_request_block_raises_modify_response_when_flag_set(): + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = {"model": "bedrock-nova-micro"} + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.make_bedrock_api_request( + source="INPUT", + messages=[{"role": "user", "content": "My name is John Doe"}], + request_data=request_data, + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + assert exc_info.value.model == "bedrock-nova-micro" + assert exc_info.value.guardrail_name == "test-bedrock-guard" + + +@pytest.mark.asyncio +async def test_make_bedrock_api_request_block_raises_http_400_when_flag_unset(): + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=False, + ) + + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(HTTPException) as exc_info: + await guardrail.make_bedrock_api_request( + source="INPUT", + messages=[{"role": "user", "content": "hi"}], + request_data={"model": "bedrock-nova-micro"}, + ) + + assert exc_info.value.status_code == 400 + + +@pytest.mark.asyncio +async def test_async_pre_call_hook_propagates_modify_response_on_block(): + """pre_call: block with disable_exception_on_block=True must raise + ModifyResponseException so the endpoint handler returns 200 with the block + message. Before LIT-4186 the exception was swallowed and only data + ["mock_response"] was mutated, which the unified pre_call path never read + (surfaced as HTTP 500).""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "My name is John Doe"}], + } + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.async_pre_call_hook( + user_api_key_dict=UserAPIKeyAuth(), + cache=DualCache(), + data=request_data, + call_type="acompletion", + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + # No `mock_response` mutation: the old broken contract must be gone + # (route_request unpacks kwargs before this hook runs, so `mock_response` + # would never reach the LLM call anyway). + assert "mock_response" not in request_data + + +@pytest.mark.asyncio +async def test_async_moderation_hook_propagates_modify_response_on_block(): + """during_call: block must raise ModifyResponseException from the moderation + task so the surrounding asyncio.gather cancels the LLM call, instead of + the old behavior of swallowing the block and letting the model call proceed + (LIT-4186 symptom 2: silent bypass, model billed).""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "My name is John Doe"}], + } + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.async_moderation_hook( + data=request_data, + user_api_key_dict=UserAPIKeyAuth(), + call_type="acompletion", + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + + +@pytest.mark.asyncio +async def test_async_post_call_success_hook_attaches_original_response_on_block(): + """post_call: block must raise ModifyResponseException and attach the LLM + response to `original_response` so the synthetic block reply reports the + upstream call's real token usage instead of zero.""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "hi"}], + } + llm_response = _model_response("Hello John Doe! The capital of France is Paris.") + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.async_post_call_success_hook( + data=request_data, + user_api_key_dict=UserAPIKeyAuth(), + response=llm_response, + ) + + assert exc_info.value.original_response is llm_response + + +@pytest.mark.asyncio +async def test_apply_guardrail_propagates_modify_response_on_block(): + """apply_guardrail (unified path used by pre_call / /apply_guardrail + endpoint) must let ModifyResponseException propagate as-is so the endpoint + handler catches it and returns a 200.""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + with patch.object( + guardrail, "make_bedrock_api_request", new_callable=AsyncMock + ) as mock_api: + mock_api.side_effect = ModifyResponseException( + message="Sorry, the model cannot answer this question.", + model="bedrock-nova-micro", + request_data={}, + guardrail_name="test-bedrock-guard", + ) + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.apply_guardrail( + inputs={"texts": ["My name is John Doe"]}, + request_data={"model": "bedrock-nova-micro"}, + input_type="request", + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + + +@pytest.mark.asyncio +async def test_streaming_post_call_block_yields_synthetic_stream_not_raise(): + """LIT-4186 regression: with disable_exception_on_block=True, streaming + post_call blocks must be delivered as a synthetic stream (finish_reason= + content_filter, block message as content), NOT raised. Pre-fix the local + handler already produced this shape; the LIT-4186 refactor briefly turned + it into an SSE 500 by letting ModifyResponseException escape the streaming + generator. This test locks in the correct streaming contract. + """ + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + async def _stream(): + yield ModelResponseStream( + choices=[ + StreamingChoices( + index=0, + delta=Delta(role="assistant", content="Coffee is a popular"), + ) + ] + ) + yield ModelResponseStream( + choices=[StreamingChoices(index=0, delta=Delta(content=" beverage."), finish_reason="stop")] + ) + + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post, + patch.object(guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1")), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + chunks = [ + c + async for c in guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=UserAPIKeyAuth(), + response=_stream(), + request_data={"model": "bedrock-nova-micro"}, + ) + ] + + assert chunks, "streaming block should yield synthetic chunks, not error out" + assembled_content = "".join( + (c.choices[0].delta.content or "") + for c in chunks + if getattr(c, "choices", None) and getattr(c.choices[0], "delta", None) + ) + assert assembled_content == "Sorry, the model cannot answer this question." + assert chunks[-1].choices[0].finish_reason == "content_filter" + + +@pytest.mark.asyncio +async def test_streaming_post_call_block_preserves_upstream_usage(): + """LIT-4186: streaming block must report the usage the upstream LLM call + actually consumed. Non-streaming blocks carry it via original_response + + _blocked_response_usage in the endpoint handler; streaming has to copy it + onto the synthetic ModelResponse directly since the exception can't escape + the SSE generator. Without this, clients see accurate billing on + non-streaming blocks and zero on streaming blocks -- silent revenue leak.""" + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices, Usage + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + async def _stream_with_usage(): + # Terminal chunk carrying usage, as OpenAI-style streams do with + # stream_options={"include_usage": True}. stream_chunk_builder + # aggregates this into the assembled ModelResponse's .usage. + yield ModelResponseStream( + choices=[StreamingChoices(index=0, delta=Delta(role="assistant", content="Coffee is delicious"))] + ) + yield ModelResponseStream( + choices=[StreamingChoices(index=0, delta=Delta(content=""), finish_reason="stop")], + usage=Usage(prompt_tokens=42, completion_tokens=17, total_tokens=59), + ) + + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post, + patch.object(guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1")), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + chunks = [ + c + async for c in guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=UserAPIKeyAuth(), + response=_stream_with_usage(), + request_data={"model": "bedrock-nova-micro"}, + ) + ] + + # Find the chunk carrying usage (MockResponseIterator emits it on the + # terminating chunk when the source ModelResponse has .usage set) + usage_chunks = [c for c in chunks if getattr(c, "usage", None) is not None] + assert usage_chunks, "streaming block should carry the upstream call's usage on at least one chunk" + reported_usage = usage_chunks[-1].usage + assert reported_usage.prompt_tokens == 42 + assert reported_usage.completion_tokens == 17 + assert reported_usage.total_tokens == 59 From bff2c952e0339a736f451797974f507e6ccbda48 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 13:19:56 -0700 Subject: [PATCH 067/399] fix(ui): key the edit form's browser-held token handling off the effective auth type The edit form decided auth mode from the saved mcpServer.auth_type in fetchTools while the authorize flow used the current form value, so a token authorized after switching the form to a client-forwarded mode was never forwarded as the x-mcp header until the server was saved. A shared getEffectiveAuthType (form value falling back to the saved record) is now the single decision point for token receipt and tool loading The save path classified the staged token with getMcpOAuthMode, which returns null for true_passthrough and oauth_delegate, so the staged token was dropped on save instead of being committed to sessionStorage the way the create form's submit path does. The passthrough branch now also covers the client-forwarded modes; the token still never enters the server row --- .../mcp_tools/mcp_server_edit.test.tsx | 70 +++++++++++++++++++ .../components/mcp_tools/mcp_server_edit.tsx | 17 +++-- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index 0579a3208d1..d55f993b926 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -1173,6 +1173,76 @@ describe("MCPServerEdit (OAuth token persistence on save)", () => { expect(onSuccess).not.toHaveBeenCalled(); }); + it.each([["true_passthrough"], ["oauth_delegate"]])( + "persists the staged token to sessionStorage on save for the %s mode", + async (authType) => { + // Regression: the save path classified the staged token with getMcpOAuthMode, which returns + // null for the client-forwarded modes, so setToken was never called and the browser-held + // token was dropped on save; the create form's submit path already committed it. + mockOauth.tokenResponse = { access_token: "cf-tok", expires_in: 1800, token_type: "bearer" }; + vi.mocked(networking.updateMCPServer).mockResolvedValue({ + ...interactiveOAuthServer, + auth_type: authType, + }); + + render( + , + ); + + await act(async () => { + fireEvent.click(screen.getAllByRole("button", { name: "Save Changes" })[0]); + }); + + await waitFor(() => { + expect(mockSetToken).toHaveBeenCalledWith( + "oauth_server_1", + expect.objectContaining({ access_token: "cf-tok" }), + "user-1", + ); + }); + expect(networking.storeMCPOAuthUserCredential).not.toHaveBeenCalled(); + const [, payload] = vi.mocked(networking.updateMCPServer).mock.calls[0]; + expect(payload.credentials).toBeUndefined(); + }, + ); + + it("forwards a newly authorized browser-held token for tool loading before the form is saved", async () => { + // Regression: fetchTools keyed the browser-held decision off the saved mcpServer.auth_type, so + // after switching the form to true_passthrough and authorizing, the fresh token was not sent as + // the x-mcp header until the server was saved. + vi.mocked(networking.listMCPTools).mockResolvedValue({ tools: [], error: null }); + mockIsTokenValid.mockReturnValue(false); + + render( + , + ); + + await selectAntOption("Authentication", "OAuth Delegate (client-supplied upstream token)"); + mockOauth.tokenResponse = { access_token: "fresh-tok", token_type: "bearer" }; + await selectAntOption("Authentication", "True Passthrough (no LiteLLM auth)"); + + await waitFor(() => { + const withHeaders = vi + .mocked(networking.listMCPTools) + .mock.calls.find(([, , headers]) => headers && JSON.stringify(headers).includes("fresh-tok")); + expect(withHeaders).toBeTruthy(); + }); + }); + it("persists the passthrough token to sessionStorage on save after authorize", async () => { mockOauth.tokenResponse = { access_token: "pt-tok", expires_in: 1800, token_type: "bearer" }; vi.mocked(networking.updateMCPServer).mockResolvedValue({ diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 59d7b28aacb..ee7938d2904 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -131,6 +131,11 @@ const MCPServerEdit: React.FC = ({ } }; + // The auth mode every decision must key off: the admin's in-flight form selection wins over the + // saved record, so authorizing, loading tools, and saving all agree with what the form shows. Paths + // that read only mcpServer.auth_type go stale the moment the admin switches modes in the form. + const getEffectiveAuthType = () => form.getFieldValue("auth_type") ?? mcpServer.auth_type; + const { startOAuthFlow, status: oauthStatus, @@ -178,8 +183,7 @@ const MCPServerEdit: React.FC = ({ return; } - const effectiveAuthType = form.getFieldValue("auth_type") ?? mcpServer.auth_type; - if (isClientForwardedTokenMode(effectiveAuthType)) { + if (isClientForwardedTokenMode(getEffectiveAuthType())) { const browserHeldToken = { access_token: token.access_token, expires_in: token.expires_in, @@ -388,7 +392,7 @@ const MCPServerEdit: React.FC = ({ oauth2_flow: mcpServer.oauth2_flow, delegate_auth_to_upstream: mcpServer.delegate_auth_to_upstream, }) === "passthrough"; - const isBrowserHeldTokenMode = isClientForwardedTokenMode(mcpServer.auth_type); + const isBrowserHeldTokenMode = isClientForwardedTokenMode(getEffectiveAuthType()); if (isPassthrough || isBrowserHeldTokenMode) { const token = oauthTokenResponse?.access_token ?? @@ -749,8 +753,9 @@ const MCPServerEdit: React.FC = ({ const updated = await updateMCPServer(accessToken, payload); // Persist the token staged via "Authorize & Fetch" (mirrors the create flow's - // commit-on-submit): OBO writes the per-user token to the DB, passthrough keeps - // it in sessionStorage. M2M/static auth resolve server-side and need neither. + // commit-on-submit): OBO writes the per-user token to the DB; legacy passthrough and the + // client-forwarded modes (true_passthrough / oauth_delegate) keep it in sessionStorage and + // never in the server row. M2M/static auth resolve server-side and need neither. if (oauthTokenResponse?.access_token) { const oauthMode = getMcpOAuthMode({ auth_type: restValues.auth_type, @@ -767,7 +772,7 @@ const MCPServerEdit: React.FC = ({ scopes: typeof scope === "string" && scope ? scope.split(" ") : undefined, }; await storeMCPOAuthUserCredential(accessToken, mcpServer.server_id, oauthCredentialPayload); - } else if (oauthMode === "passthrough") { + } else if (oauthMode === "passthrough" || isClientForwardedTokenMode(restValues.auth_type)) { const browserHeldToken = { access_token: oauthTokenResponse.access_token, expires_in: oauthTokenResponse.expires_in, From 97d09512969f2adf5b39863cc18e9a75b0915d78 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 9 Jul 2026 16:39:47 -0400 Subject: [PATCH 068/399] test(e2e): make dynamic model provisioning robust on split deployments (#32670) create_model now waits until the new deployment is servable on the data plane (polls /v1/models) before returning, instead of assuming /model/new makes it instantly callable. On a split control/data-plane proxy the gateway only sees a model after its next DB reload, so an immediate call raced the reload and 400'd with "Invalid model name passed" (embeddings, responses, messages, ocr, ...). It also stops pinning model_info.id to the model_name, letting the proxy assign a unique model_id. Re-registering a fixed-name deployment (the batch suite's openai-batch et al.) after a failed teardown no longer collides on the model_id unique constraint (prisma UniqueViolationError surfaced as the generic 500 "Failed to add model to db", erroring every batch_lifecycle case at setup) --- tests/e2e/e2e_gateway.py | 49 +++++++++++++++++++++++--- tests/e2e/models.py | 18 +++++++++- tests/e2e/test_e2e_gateway.py | 66 ++++++++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/tests/e2e/e2e_gateway.py b/tests/e2e/e2e_gateway.py index 055d06d1c79..d62c9c4b17b 100644 --- a/tests/e2e/e2e_gateway.py +++ b/tests/e2e/e2e_gateway.py @@ -42,6 +42,7 @@ from models import ( ModelMode, ModelNewBody, ModelNewResponse, + ModelsListResponse, OcrBody, OcrResponse, SpendLogRow, @@ -125,21 +126,59 @@ class Gateway: litellm_params: LiteLLMParamsBody, mode: ModelMode | None = None, ) -> str: - """Register a deployment under `model_name` (id == model_name) and return the - model_id. add_deployment runs synchronously in /model/new, so the model is - callable as soon as this returns.""" - return unwrap( + """Register a deployment under `model_name` and return its proxy-assigned + model_id, once the model is actually servable on the data plane. + + /model/new is a control-plane route; in a split control/data-plane + deployment the gateway (data plane, which serves /chat, /ocr, ...) only + picks the new model up on its next DB reload, so a call issued the instant + this returns can race the reload and 400 with "Invalid model name passed". + We therefore poll the data-plane /v1/models until the model appears before + handing back, so callers can invoke it immediately. In the monolithic case + it is already present on the first poll, so this adds one request.""" + model_id = unwrap( self.transport.post( "/model/new", headers=self.transport.master, json=ModelNewBody( model_name=model_name, litellm_params=litellm_params, - model_info=ModelInfoBody(id=model_name, mode=mode), + model_info=ModelInfoBody(mode=mode), ), response_type=ModelNewResponse, ) ).model_id + self._await_model_servable(model_name) + return model_id + + def _await_model_servable(self, model_name: str) -> None: + """Block until the data plane lists `model_name`, or fail loudly if it does + not within poll_timeout (a real propagation/config problem, surfaced here + instead of as a downstream "Invalid model name passed").""" + deadline = time.monotonic() + self.poll_timeout + last_result: Result[ModelsListResponse] | None = None + while time.monotonic() < deadline: + last_result = self.transport.get( + "/v1/models", + headers=self.transport.master, + params=NoBody(), + response_type=ModelsListResponse, + ) + if isinstance(last_result, Success) and any( + entry.id == model_name for entry in last_result.data.data + ): + return + time.sleep(self.poll_interval) + last_error = ( + f"; last /v1/models poll did not succeed: {last_result}" + if last_result is not None and not isinstance(last_result, Success) + else "" + ) + raise AssertionError( + f"model {model_name!r} was created but never became servable on the data " + f"plane within {self.poll_timeout}s of /model/new (control/data-plane " + f"propagation or STORE_MODEL_IN_DB reload issue){last_error}" + ) def delete_model(self, model_id: str) -> None: result = self.transport.post( diff --git a/tests/e2e/models.py b/tests/e2e/models.py index 0490db286ea..f287058b313 100644 --- a/tests/e2e/models.py +++ b/tests/e2e/models.py @@ -394,7 +394,11 @@ ModelMode = Literal["batch", "realtime", "image_generation"] class ModelInfoBody(BaseModel): - id: str + # id is left unset so the proxy assigns a unique model_id per deployment. + # Pinning it to the model_name made re-registrations of a fixed-name model + # (e.g. the batch suite's openai-batch) collide on the model_id unique + # constraint when a prior run's teardown had not removed the row. + id: str | None = None mode: ModelMode | None = None @@ -410,6 +414,18 @@ class ModelNewResponse(BaseModel): model_id: str +class ModelListEntry(BaseModel): + id: str + + +class ModelsListResponse(BaseModel): + """GET /v1/models on the data plane: the deployments the gateway can actually + serve right now. Used to confirm a freshly created model has propagated from + the control plane before a test calls it.""" + + data: tuple[ModelListEntry, ...] = () + + class ModelDeleteBody(BaseModel): id: str diff --git a/tests/e2e/test_e2e_gateway.py b/tests/e2e/test_e2e_gateway.py index a6dcc6112d6..9a9aa2fd2cc 100644 --- a/tests/e2e/test_e2e_gateway.py +++ b/tests/e2e/test_e2e_gateway.py @@ -10,6 +10,7 @@ signature drift fails here instead of in a live stage run. from dataclasses import dataclass, field +import pytest from pydantic import BaseModel from batches.batch_client import BatchClient @@ -21,26 +22,38 @@ from e2e_http import ( Result, StreamingResponse, Success, + UnknownApiError, ) from models import ( LiteLLMParamsBody, ModelDeleteBody, ModelNewBody, ModelNewResponse, + ModelsListResponse, ) @dataclass class _RecordingTransport: """Typed fake fulfilling the Transport protocol; records every post and - answers with a canned success so the test asserts on what was sent.""" + answers with a canned success so the test asserts on what was sent. + + `get("/v1/models")` reports a created model as servable only after + `servable_after_gets` polls, so a test can drive the data-plane wait in + create_model.""" posts: list[tuple[str, BaseModel]] = field(default_factory=list) + servable_after_gets: int = 0 + models_error: UnknownApiError | None = None + model_gets: int = 0 + _created: list[str] = field(default_factory=list) def post[R: BaseModel]( self, path: str, *, headers: BaseModel, json: BaseModel, response_type: type[R] ) -> Result[R]: self.posts.append((path, json)) + if path == "/model/new" and isinstance(json, ModelNewBody): + self._created.append(json.model_name) payload = ( {"model_id": "registered-id"} if response_type is ModelNewResponse else {} ) @@ -70,7 +83,15 @@ class _RecordingTransport: params: BaseModel, response_type: type[R], ) -> Result[R]: - raise AssertionError("get is not part of model management") + if path == "/v1/models" and response_type is ModelsListResponse: + self.model_gets += 1 + if self.models_error is not None: + return self.models_error + visible = self._created if self.model_gets > self.servable_after_gets else [] + return Success( + data=response_type.model_validate({"data": [{"id": name} for name in visible]}) + ) + raise AssertionError(f"unexpected get: {path}") def delete[R: BaseModel]( self, path: str, *, headers: BaseModel, json: BaseModel, response_type: type[R] @@ -106,7 +127,7 @@ class _RecordingTransport: def test_gateway_create_model_registers_deployment_and_returns_model_id() -> None: transport = _RecordingTransport() - gateway = Gateway(transport=transport) + gateway = Gateway(transport=transport, poll_interval=0.0) model_id = gateway.create_model( "e2e-test-model", LiteLLMParamsBody(model="openai/gpt-4o-mini") @@ -117,13 +138,48 @@ def test_gateway_create_model_registers_deployment_and_returns_model_id() -> Non assert path == "/model/new" assert isinstance(body, ModelNewBody) assert body.model_name == "e2e-test-model" - assert body.model_info.id == "e2e-test-model" + # No pinned model_id: the proxy assigns a unique one, so a fixed-name model + # re-registered after a failed teardown can't collide on the id constraint. + assert body.model_info.id is None assert body.model_info.mode is None + # It confirmed data-plane visibility before returning. + assert transport.model_gets >= 1 + + +def test_gateway_create_model_waits_until_servable_on_the_data_plane() -> None: + # The model shows up on /v1/models only on the third poll (simulating the + # gateway's delayed DB reload in a split deployment); create_model must keep + # polling instead of returning after /model/new. + transport = _RecordingTransport(servable_after_gets=2) + gateway = Gateway(transport=transport, poll_interval=0.0) + + gateway.create_model("e2e-late-model", LiteLLMParamsBody(model="openai/gpt-4o-mini")) + + assert transport.model_gets == 3 + + +def test_gateway_create_model_fails_loudly_when_never_servable() -> None: + transport = _RecordingTransport(servable_after_gets=10**9) + gateway = Gateway(transport=transport, poll_timeout=0.05, poll_interval=0.0) + + with pytest.raises(AssertionError, match="never became servable"): + gateway.create_model("e2e-ghost-model", LiteLLMParamsBody(model="openai/gpt-4o-mini")) + + +def test_gateway_create_model_surfaces_the_last_data_plane_error() -> None: + transport = _RecordingTransport( + models_error=UnknownApiError(status_code=503, body="data plane down") + ) + gateway = Gateway(transport=transport, poll_timeout=0.05, poll_interval=0.0) + + with pytest.raises(AssertionError, match="data plane down") as excinfo: + gateway.create_model("e2e-flaky-model", LiteLLMParamsBody(model="openai/gpt-4o-mini")) + assert "503" in str(excinfo.value) def test_batch_client_create_model_registers_a_batch_mode_deployment() -> None: transport = _RecordingTransport() - client = BatchClient(gateway=Gateway(transport=transport)) + client = BatchClient(gateway=Gateway(transport=transport, poll_interval=0.0)) model_id = client.create_model( "e2e-batch-model", LiteLLMParamsBody(model="openai/gpt-4o-mini") From d1a79f79713d700d2b685b76ae2c262853bb1fa7 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Thu, 9 Jul 2026 13:48:20 -0700 Subject: [PATCH 069/399] fix(ui): rename Virtual Keys 'Key Hash' filter label to 'Key ID' (#32672) --- .../src/components/VirtualKeysPage/VirtualKeysTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index 00f4304c8a9..cae6dc54df5 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -578,7 +578,7 @@ export function VirtualKeysTable() { }, { name: "Key Hash", - label: "Key Hash", + label: "Key ID", isSearchable: false, }, ]; From 5cf269088cca64f9fa16faeba4dedd00bf48486d Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Thu, 9 Jul 2026 13:48:47 -0700 Subject: [PATCH 070/399] fix(proxy): capture logging_obj before post_call_failure_hook pops it in ModifyResponseException streaming path (#32665) * fix(guardrails/bedrock): honor disable_exception_on_block by raising ModifyResponseException The Bedrock-specific GuardrailInterventionNormalStringError predates the unified guardrails refactor and no proxy code path handles it, so a block with the flag set surfaced as an uncaught Exception -> HTTP 500 in pre_call mode and was silently discarded in during_call mode (model call proceeded in the parallel asyncio.gather; the block hook's data["mock_response"] mutation happened after route_request had already unpacked kwargs). Convert the block to ModifyResponseException at the raise site inside make_bedrock_api_request. That exception is the industry-standard proxy contract already caught in proxy_server, anthropic_endpoints, response_api _endpoints, and pass_through_endpoints; it turns into a 200 response with finish_reason=content_filter and the block message as content, which is exactly what the flag was documented to yield. Post-call blocks attach the LLM response to original_response so the synthetic reply reports the upstream call's real token usage instead of zero. Deletes the now-orphaned GuardrailInterventionNormalStringError class and the dead create_guardrail_blocked_response / mock_response plumbing in the Bedrock hooks; updates the existing tests that had locked in the buggy contract. Resolves LIT-4186 * chore(guardrails/bedrock): drop dead str branch in _update_messages_with_updated_bedrock_guardrail_response Follow-up to the disable_exception_on_block fix. That method used to receive either a BedrockGuardrailResponse or a plain string (the block message, when the flag was set). Now that a block always raises ModifyResponseException before this method runs, the string branch is unreachable; tighten the type to BedrockGuardrailResponse and delete the guard. * fix(guardrails/bedrock): streaming post_call block yields synthetic stream instead of surfacing as SSE 500 Regression from the LIT-4186 refactor: pre-refactor, the streaming post_call iterator caught GuardrailInterventionNormalStringError locally and replaced the assembled response with a synthetic content-filter message, then re-emitted it as chunks via MockResponseIterator. After the refactor the exception was re-raised as ModifyResponseException, which async_streaming_data_generator serializes as a proxy 500 error frame because the SSE response headers are already flushed by the time the block fires. Non-streaming paths still let ModifyResponseException propagate to the endpoint handler (which converts it into a 200). Streaming can't do that, so keep the local synthesis: on the exception, rebind the assembled response to a ModelResponse whose single choice carries the block message as content and finish_reason=content_filter, and let the downstream MockResponseIterator emit it as chunks. Same shape a non-streaming block produces. Adds a mapped-file regression test that mutation-kills the raise behavior and locks in the synthetic-stream contract. * fix(guardrails/bedrock): preserve upstream usage on streaming post_call block Non-streaming post_call blocks report the upstream LLM call's real token usage via ModifyResponseException.original_response, which the endpoint handler unwraps through _blocked_response_usage. Streaming post_call synthesizes its own ModelResponse locally (the exception can't escape the SSE generator), and previously left .usage unset, so the client saw accurate billing on non-streaming blocks and zero on streaming blocks -- silent revenue leak. Copy the assembled response's .usage onto the synthetic block response before yielding. Pre-refactor code had the same gap (create_guardrail_blocked_response never set usage); this is a net improvement, not a regression fix. * fix(proxy): capture logging_obj before post_call_failure_hook pops it in ModifyResponseException streaming path post_call_failure_hook removes litellm_logging_obj from request_data before iterating callbacks (it's not serialisable). The streaming branch of the ModifyResponseException handler read it from _data after that call, so it always received None and CustomStreamWrapper.__init__ crashed with AttributeError: NoneType has no attribute model_call_details. Capture it before the hook runs so the streaming path gets a valid object. Co-authored-by: Mateo Wang * test(proxy): add regression for streaming ModifyResponseException logging_obj capture Covers the bug where logging_obj was read from request_data after post_call_failure_hook had already popped it, causing CustomStreamWrapper to crash with AttributeError. Co-authored-by: Mateo Wang * test(proxy): drive real chat_completion in ModifyResponseException streaming logging_obj regression The original test inlined the fix pattern (capture before pop) in its own body rather than calling the actual chat_completion handler in proxy_server.py, so a revert of the fix left the test passing. Confirmed via mutation check: reverting the two-line source fix and re-running left the test green. Rewrite the test to drive chat_completion directly: - patch _read_request_body so chat_completion sees the seeded dict - patch ProxyBaseLLMRequestProcessing.base_process_llm_request to raise ModifyResponseException with the same request_data - patch proxy_logging_obj so post_call_failure_hook mutates the dict the way production does (pops litellm_logging_obj) - intercept CustomStreamWrapper.__init__ and assert logging_obj is the non-None object seeded in request_data Mutation-verified: reverting the source fix now surfaces the exact production crash inside CustomStreamWrapper's __init__ (AttributeError: NoneType has no attribute model_call_details) rather than a silently-passing test. Addresses Greptile P1 on PR #32665. --------- Co-authored-by: Mateo Wang --- litellm/proxy/proxy_server.py | 4 +- .../test_bedrock_guardrails.py | 102 ++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 13e2d4f1252..4114bda47c9 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -8544,6 +8544,8 @@ async def chat_completion( except ModifyResponseException as e: # Guardrail flagged content in passthrough mode - return 200 with violation message _data = e.request_data + # Capture logging_obj before post_call_failure_hook pops it from _data. + _logging_obj = _data.get("litellm_logging_obj") await proxy_logging_obj.post_call_failure_hook( user_api_key_dict=user_api_key_dict, original_exception=e, @@ -8563,7 +8565,7 @@ async def chat_completion( completion_stream=_iterator, model=e.model, custom_llm_provider="cached_response", - logging_obj=_data.get("litellm_logging_obj", None), + logging_obj=_logging_obj, ) selected_data_generator = select_data_generator( response=_streaming_response, diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py index bf237d8017b..15827b80bcf 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py @@ -3172,3 +3172,105 @@ async def test_streaming_post_call_block_preserves_upstream_usage(): assert reported_usage.prompt_tokens == 42 assert reported_usage.completion_tokens == 17 assert reported_usage.total_tokens == 59 + + +############################################################################### +# Regression test for the streaming logging_obj bug found during live testing. +# +# post_call_failure_hook (proxy_server.py) pops litellm_logging_obj from +# request_data before invoking callbacks ("not serialisable"). The streaming +# branch of the ModifyResponseException handler previously read logging_obj +# from _data AFTER that call, always getting None, causing: +# AttributeError: 'NoneType' object has no attribute 'model_call_details' +# inside CustomStreamWrapper.__init__, which surfaced as HTTP 500. +# +# The fix captures logging_obj BEFORE calling post_call_failure_hook. +# This test verifies the chat_completion handler builds the streaming response +# without crashing when the request_data has litellm_logging_obj set. +############################################################################### + + +@pytest.mark.asyncio +async def test_chat_completion_modify_response_exception_streaming_logging_obj_not_none(): + """Regression: streaming ModifyResponseException handler in chat_completion + must capture logging_obj before post_call_failure_hook pops it from + request_data. Previously this caused CustomStreamWrapper.__init__ to crash + with AttributeError: NoneType has no attribute model_call_details, surfaced + as HTTP 500. + + Drives the real chat_completion handler with base_process_llm_request + mocked to raise ModifyResponseException, so a revert of the fix in + proxy_server.py causes this test to fail. + """ + import litellm + from litellm.exceptions import ModifyResponseException + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.proxy_server import chat_completion + + fake_logging_obj = MagicMock() + fake_logging_obj.model_call_details = {"litellm_params": {}} + + request_data: dict = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "how do I become an admin"}], + "stream": True, + "litellm_logging_obj": fake_logging_obj, + } + + exc = ModifyResponseException( + message="Sorry, the model cannot answer this question.", + model="bedrock-nova-micro", + request_data=request_data, + guardrail_name="test-guard", + ) + + fastapi_request = MagicMock() + fastapi_request.headers = {} + fastapi_response = MagicMock() + user_api_key_dict = UserAPIKeyAuth() + + async def _fake_post_call_failure_hook(**_kwargs): + # Match production: pop the logging obj from request_data before + # callbacks iterate (litellm/proxy/utils.py: "Remove before callbacks + # iterate — not serialisable"). + _kwargs["request_data"].pop("litellm_logging_obj", None) + + mock_proxy_logging = MagicMock() + mock_proxy_logging.post_call_failure_hook = AsyncMock(side_effect=_fake_post_call_failure_hook) + + captured_logging_obj: list = [] + original_init = litellm.CustomStreamWrapper.__init__ + + def _patched_init(self, *args, **kwargs): + captured_logging_obj.append(kwargs.get("logging_obj")) + original_init(self, *args, **kwargs) + + async def _raise_modify_response(*_args, **_kwargs): + raise exc + + with ( + patch("litellm.proxy.proxy_server._read_request_body", AsyncMock(return_value=request_data)), + patch("litellm.proxy.proxy_server.proxy_logging_obj", mock_proxy_logging), + patch( + "litellm.proxy.proxy_server.ProxyBaseLLMRequestProcessing.base_process_llm_request", + _raise_modify_response, + ), + patch.object(litellm.CustomStreamWrapper, "__init__", _patched_init), + ): + response = await chat_completion( + request=fastapi_request, + fastapi_response=fastapi_response, + model=None, + user_api_key_dict=user_api_key_dict, + ) + + assert captured_logging_obj, "chat_completion did not construct CustomStreamWrapper on the streaming block path" + assert captured_logging_obj[0] is fake_logging_obj, ( + "chat_completion passed logging_obj=None to CustomStreamWrapper; " + "the streaming ModifyResponseException handler must capture logging_obj " + "before post_call_failure_hook pops it from request_data" + ) + # A streaming block returns a StreamingResponse; if the fix were reverted, + # CustomStreamWrapper would raise AttributeError inside __init__ and this + # call would never reach here. + assert response is not None From 43726f2d0be74df2a381e28495f2e3819384c705 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 13:51:03 -0700 Subject: [PATCH 071/399] refactor(ui): useTestMCPConnection uses the shared isClientForwardedTokenMode helper The helper extraction missed this call site, leaving an inline duplicate of the two-mode check that could drift from the shared definition --- ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx index 3208b6b02b2..d27e1ca8bf9 100644 --- a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx +++ b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from "react"; import { testMCPToolsListRequest } from "../components/networking"; -import { AUTH_TYPE, OAUTH_FLOW, TRANSPORT } from "@/components/mcp_tools/types"; +import { AUTH_TYPE, OAUTH_FLOW, TRANSPORT, isClientForwardedTokenMode } from "@/components/mcp_tools/types"; interface MCPServerConfig { server_id?: string; @@ -56,8 +56,7 @@ export const useTestMCPConnection = ({ // Check if we have the minimum required fields to fetch tools const isM2MOAuth = formValues.auth_type === AUTH_TYPE.OAUTH2 && formValues.oauth_flow_type === OAUTH_FLOW.M2M; - const isBrowserHeldTokenMode = - formValues.auth_type === AUTH_TYPE.TRUE_PASSTHROUGH || formValues.auth_type === AUTH_TYPE.OAUTH_DELEGATE; + const isBrowserHeldTokenMode = isClientForwardedTokenMode(formValues.auth_type); const requiresOAuthToken = (formValues.auth_type === AUTH_TYPE.OAUTH2 && !isM2MOAuth) || isBrowserHeldTokenMode; const isOpenAPITransport = formValues.transport === TRANSPORT.OPENAPI; const hasEndpoint = isOpenAPITransport ? !!formValues.spec_path : !!formValues.url; From 8519d7fc24973457fc66e6cd25a504af6f1b8208 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 9 Jul 2026 16:54:45 -0400 Subject: [PATCH 072/399] test: litellm fix failing tests (#32577) * fix: rust ocr tests finally pass * fix: move realtime dir * fix(realtime): normalize azure realtime api_base to host for Foundry endpoints The azure realtime handler appended the realtime path to api_base verbatim, so a Foundry base carrying a project path (.../api/projects/) produced an invalid realtime URL and the websocket handshake hung. Normalize api_base to scheme and host before building the realtime path so both Azure OpenAI and Foundry bases connect Point the e2e realtime azure deployment at the GA gpt-realtime model and stop passing the os.environ refs the realtime path never unwraps, resolving them from the gateway env by name instead. Drop the local docker-compose scaffolding from the tree * test(e2e): add Gateway.list_files and list_fine_tuning_jobs for the discovery suite The discovery endpoints suite calls client.gateway.list_files and list_fine_tuning_jobs, which did not exist on Gateway, so both tests errored with AttributeError before reaching the proxy. Add the two GET wrappers using the existing FileListResponse / FineTuningJobsResponse models * revert(realtime): drop azure realtime api_base host-normalization The azure realtime handshake failure was a config issue, not a litellm bug: the realtime base was set to the Azure AI Foundry project endpoint (.../api/projects/

), but the OpenAI-compatible realtime route lives at the resource root. litellm correctly appends the realtime path to whatever base it is given, so pointing the realtime deployment at the resource root is the fix and no core change is needed * fix(ocr): route azure_ai doc-intelligence to its own endpoint at the source get_llm_provider inherits AZURE_AI_API_BASE into api_base for every azure_ai/* OCR model, but Azure Document Intelligence is a separate resource reached via AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT, so doc-intelligence requests went to the wrong host. Stop inheriting the azure_ai base for doc-intelligence models so api_base stays unset and both the rust bridge and the python get_complete_url fall back to the document-intelligence endpoint. This drops the earlier _rust_bridge_api_base reorder, which only covered the rust path and let the env silently override an explicit api_base * refactor(ocr): consolidate azure doc-intelligence detection; keep explicit api_base Extract is_azure_document_intelligence_model as the single source of truth for the azure_ai doc-intelligence sub-route so the check is no longer duplicated across _prepare_ocr_request and _rust_bridge_api_base, and gate the dynamic_api_base suppression on the caller not supplying an api_base so an explicit endpoint is always honoured. Restore xai to the realtime PROVIDERS as a documented disabled entry instead of dropping it silently, and add a regression test pinning doc-intelligence api_base resolution. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Mubashir Osmani Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/llms/azure_ai/ocr/common_utils.py | 13 +- litellm/llms/deepseek/chat/transformation.py | 18 +- litellm/ocr/main.py | 15 +- tests/e2e/bob_the_builder.py | 247 ++++++++++++++++++ tests/e2e/conftest.py | 7 + tests/e2e/docker-compose.yml | 4 + tests/e2e/e2e_gateway.py | 21 ++ .../realtime/REALTIME_COVERAGE_MATRIX.md | 66 +++++ .../e2e/llm_translation/realtime/conftest.py | 37 +++ .../fixtures/weather_question_24k.wav | Bin .../realtime/pipecat_service.py | 0 .../realtime/realtime_client.py | 96 +++++-- .../realtime/test_realtime_e2e.py | 14 +- .../test_realtime_pipecat_audio_e2e.py | 23 +- .../realtime/test_realtime_pipecat_e2e.py | 8 +- .../test_deepseek_reasoning_e2e.py | 29 +- .../e2e/llm_translation/test_ocr_rust_e2e.py | 23 +- .../test_provider_features_e2e.py | 27 +- tests/e2e/models.py | 1 + .../e2e/realtime/REALTIME_COVERAGE_MATRIX.md | 55 ---- tests/e2e/realtime/conftest.py | 20 -- ...cr_azure_document_intelligence_api_base.py | 85 ++++++ 22 files changed, 636 insertions(+), 173 deletions(-) create mode 100644 tests/e2e/bob_the_builder.py create mode 100644 tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md create mode 100644 tests/e2e/llm_translation/realtime/conftest.py rename tests/e2e/{ => llm_translation}/realtime/fixtures/weather_question_24k.wav (100%) rename tests/e2e/{ => llm_translation}/realtime/pipecat_service.py (100%) rename tests/e2e/{ => llm_translation}/realtime/realtime_client.py (69%) rename tests/e2e/{ => llm_translation}/realtime/test_realtime_e2e.py (92%) rename tests/e2e/{ => llm_translation}/realtime/test_realtime_pipecat_audio_e2e.py (95%) rename tests/e2e/{ => llm_translation}/realtime/test_realtime_pipecat_e2e.py (97%) delete mode 100644 tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md delete mode 100644 tests/e2e/realtime/conftest.py create mode 100644 tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py diff --git a/litellm/llms/azure_ai/ocr/common_utils.py b/litellm/llms/azure_ai/ocr/common_utils.py index d1d5b80b78d..14b77338fd7 100644 --- a/litellm/llms/azure_ai/ocr/common_utils.py +++ b/litellm/llms/azure_ai/ocr/common_utils.py @@ -13,6 +13,17 @@ if TYPE_CHECKING: from litellm.llms.base_llm.ocr.transformation import BaseOCRConfig +def is_azure_document_intelligence_model(model: str) -> bool: + """Whether an azure_ai OCR model routes to Azure Document Intelligence. + + Azure AI exposes two OCR services on the same provider; the sub-route in the + model name (`azure_ai/doc-intelligence/`) selects Document Intelligence + over Mistral OCR. This is the single source of truth for that routing decision. + """ + lowered = model.lower() + return "doc-intelligence" in lowered or "documentintelligence" in lowered + + def get_azure_ai_ocr_config(model: str) -> Optional["BaseOCRConfig"]: """ Determine which Azure AI OCR configuration to use based on the model name. @@ -41,7 +52,7 @@ def get_azure_ai_ocr_config(model: str) -> Optional["BaseOCRConfig"]: from litellm.llms.azure_ai.ocr.transformation import AzureAIOCRConfig # Check for Azure Document Intelligence models - if "doc-intelligence" in model or "documentintelligence" in model: + if is_azure_document_intelligence_model(model): verbose_logger.debug(f"Routing {model} to Azure Document Intelligence OCR config") return AzureDocumentIntelligenceOCRConfig() diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 7a548136f2a..525de1476e2 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -35,7 +35,9 @@ class DeepSeekChatConfig(OpenAIGPTConfig): Map OpenAI params to DeepSeek params. Handles `thinking` and `reasoning_effort` parameters for DeepSeek reasoner models. - DeepSeek only supports `{"type": "enabled"}` - no budget_tokens like Anthropic. + DeepSeek supports `{"type": "enabled"}` and `{"type": "disabled"}` - no budget_tokens + like Anthropic. `reasoning_effort="none"` is the OpenAI-style way to ask for thinking + off, so it maps to `{"type": "disabled"}`; any other effort keeps thinking on. Reference: https://api-docs.deepseek.com/guides/thinking_mode """ @@ -47,15 +49,13 @@ class DeepSeekChatConfig(OpenAIGPTConfig): thinking_value = optional_params.pop("thinking", None) reasoning_effort = optional_params.pop("reasoning_effort", None) - # Handle thinking parameter - only accept {"type": "enabled"} - if thinking_value is not None: - if isinstance(thinking_value, dict) and thinking_value.get("type") == "enabled": - # DeepSeek only accepts {"type": "enabled"}, ignore budget_tokens - optional_params["thinking"] = {"type": "enabled"} + # Handle thinking parameter - accept both enabled and disabled, ignore budget_tokens + if isinstance(thinking_value, dict) and thinking_value.get("type") in ("enabled", "disabled"): + optional_params["thinking"] = {"type": thinking_value["type"]} - # Handle reasoning_effort - map to thinking enabled - elif reasoning_effort is not None and reasoning_effort != "none": - optional_params["thinking"] = {"type": "enabled"} + # Otherwise fall back to reasoning_effort: "none" disables, anything else enables + elif reasoning_effort is not None: + optional_params["thinking"] = {"type": "disabled" if reasoning_effort == "none" else "enabled"} return optional_params diff --git a/litellm/ocr/main.py b/litellm/ocr/main.py index 5716155361d..38f3f804e10 100644 --- a/litellm/ocr/main.py +++ b/litellm/ocr/main.py @@ -17,6 +17,9 @@ import litellm from litellm._logging import verbose_logger from litellm.constants import request_timeout from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.azure_ai.ocr.common_utils import ( + is_azure_document_intelligence_model, +) from litellm.llms.base_llm.ocr.transformation import BaseOCRConfig, OCRResponse from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler from litellm.rust_bridge import ocr as rust_ocr_bridge @@ -83,6 +86,8 @@ def _prepare_ocr_request( if doc_type not in ["document_url", "image_url"]: raise ValueError(f"Invalid document type: {doc_type}. Must be 'document_url', 'image_url', or 'file'") + caller_supplied_api_base = api_base is not None + ( model, custom_llm_provider, @@ -95,9 +100,14 @@ def _prepare_ocr_request( api_key=api_key, ) + suppress_dynamic_api_base = ( + not caller_supplied_api_base + and custom_llm_provider == "azure_ai" + and is_azure_document_intelligence_model(model) + ) if dynamic_api_key: api_key = dynamic_api_key - if dynamic_api_base: + if dynamic_api_base and not suppress_dynamic_api_base: api_base = dynamic_api_base ocr_provider_config = ProviderConfigManager.get_provider_ocr_config( @@ -191,8 +201,7 @@ def _rust_bridge_api_base( if prepared_request.api_base is not None: return prepared_request.api_base if prepared_request.custom_llm_provider == "azure_ai": - model = prepared_request.model.lower() - if "doc-intelligence" in model or "documentintelligence" in model: + if is_azure_document_intelligence_model(prepared_request.model): return resolve_secret("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT") return resolve_secret("AZURE_AI_API_BASE") return None diff --git a/tests/e2e/bob_the_builder.py b/tests/e2e/bob_the_builder.py new file mode 100644 index 00000000000..18aff2edc98 --- /dev/null +++ b/tests/e2e/bob_the_builder.py @@ -0,0 +1,247 @@ +"""Bob the builder: on a red e2e run, ask Devin to fix the failing tests. + +Wired as a ``pytest_sessionfinish`` step (see ``conftest.py``). When the run went +red and remediation is enabled, it hands the failing tests plus their captured +tracebacks to Devin *through the LiteLLM proxy's own MCP gateway* -- the same +gateway + master key the suite already uses -- so Devin files a Linear ticket per +failure and opens fix PRs. Nothing new ships in the runner pod: the proxy already +registers the ``devin`` MCP server and holds ``DEVIN_API_KEY``, injecting it +upstream, so this process only needs the proxy key it always has. + +Opt-in via ``E2E_DEVIN_REMEDIATION=1`` so a normal local ``pytest tests/e2e`` run +never spawns a Devin session. ``DEVIN_DRY_RUN=1`` prints the prompt it would send +and makes no call. Everything is best-effort: any error here is logged and +swallowed so the run's exit status still reflects the tests, not remediation. +""" + +from __future__ import annotations + +import hashlib +import os +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Protocol, cast + +import pytest +from pydantic import BaseModel, ConfigDict + +from e2e_config import MASTER_KEY, PROXY_BASE_URL +from e2e_http import Success +from transport import HttpTransport + +REMEDIATION_ENV = "E2E_DEVIN_REMEDIATION" +_LIST_PATH = "/mcp-rest/tools/list" +_CALL_PATH = "/mcp-rest/tools/call" + + +@dataclass(frozen=True, slots=True) +class Failure: + """One failed test: its pytest node id and the captured failure text.""" + + nodeid: str + detail: str + + +@dataclass(frozen=True, slots=True) +class Config: + server: str + create_tool: str + linear_team: str + target_repo: str + target_ref: str + max_failures: int + max_detail_chars: int + tags: tuple[str, ...] + dry_run: bool + + +class _NoParams(BaseModel): + pass + + +class _McpToolInfo(BaseModel): + model_config = ConfigDict(extra="allow") + server_name: str | None = None + alias: str | None = None + + +class _McpTool(BaseModel): + model_config = ConfigDict(extra="allow") + name: str + mcp_info: _McpToolInfo | None = None + + +class _McpToolsList(BaseModel): + model_config = ConfigDict(extra="allow") + tools: tuple[_McpTool, ...] = () + + +class _DevinSessionArgs(BaseModel): + prompt: str + title: str + tags: list[str] + + +class _ToolCallBody(BaseModel): + name: str + arguments: _DevinSessionArgs + + +class _ToolCallResult(BaseModel): + model_config = ConfigDict(extra="allow") + + +class _Report(Protocol): + @property + def nodeid(self) -> str: ... + + @property + def longreprtext(self) -> str: ... + + +class _TerminalReporter(Protocol): + stats: Mapping[str, Sequence[_Report]] + + +def _env(name: str, default: str) -> str: + value = os.environ.get(name, "").strip() + return value or default + + +def load_config() -> Config: + raw_tags = _env("DEVIN_TAGS", "e2e,stage") + return Config( + server=_env("DEVIN_MCP_SERVER", "devin"), + create_tool=_env("DEVIN_SESSION_TOOL", "devin_session_create"), + linear_team=_env("DEVIN_LINEAR_TEAM", "LIT"), + target_repo=_env("DEVIN_TARGET_REPO", "BerriAI/litellm"), + target_ref=_env("DEVIN_TARGET_REF", "litellm_internal_staging"), + max_failures=int(_env("DEVIN_MAX_FAILURES", "50")), + max_detail_chars=int(_env("DEVIN_MAX_DETAIL_CHARS", "3000")), + tags=tuple(t.strip() for t in raw_tags.split(",") if t.strip()), + dry_run=_env("DEVIN_DRY_RUN", "0") == "1", + ) + + +def collect_failures(session: pytest.Session, max_detail_chars: int) -> tuple[Failure, ...]: + """Pull the failed and errored tests (with their tracebacks) off the run's + terminal reporter. Returns empty when nothing failed or the reporter is + absent (e.g. a skipped, proxy-less session).""" + plugin: object = session.config.pluginmanager.getplugin("terminalreporter") + if plugin is None: + return () + reporter = cast(_TerminalReporter, plugin) + reports = (*reporter.stats.get("failed", ()), *reporter.stats.get("error", ())) + return tuple( + Failure(nodeid=r.nodeid, detail=r.longreprtext.strip()[-max_detail_chars:]) for r in reports + ) + + +def dedup_tag(failures: tuple[Failure, ...]) -> str: + """Stable short tag identifying this exact set of failing tests, so repeated + nightly runs on the same failures reference one body of work.""" + joined = "\n".join(sorted(f.nodeid for f in failures)) + return "e2e-fail-" + hashlib.sha256(joined.encode()).hexdigest()[:12] + + +def _revision() -> str: + for candidate in (Path(__file__).parent / ".litellm-revision", Path("/app/e2e/.litellm-revision")): + try: + return candidate.read_text(encoding="utf-8").strip() + except OSError: + continue + return _env("E2E_REVISION", "unknown") + + +def build_prompt(cfg: Config, failures: tuple[Failure, ...], tag: str) -> str: + shown = failures[: cfg.max_failures] + header = ( + f"The LiteLLM end-to-end suite failed on the " + f"{_env('E2E_ENVIRONMENT', 'stage')} proxy. Source repo {cfg.target_repo} " + f"at revision {_revision()} (branch {cfg.target_ref}). {len(failures)} " + f"test(s) failed" + + (f"; the first {len(shown)} are shown" if len(shown) < len(failures) else "") + + ".\n\n" + ) + task = ( + "For each failing test below:\n" + f"1. Open a Linear ticket under the {cfg.linear_team} team describing the " + "failure (test id, the assertion/error, likely cause), unless an open " + "ticket for that same test already exists -- do not create duplicates.\n" + f"2. Fix it in {cfg.target_repo}, branching off {cfg.target_ref} and " + "following the repo's CONTRIBUTING and CLAUDE.md conventions (meaningful " + "regression coverage, conventional commits, run the suite locally), then " + "open a PR that references the Linear ticket.\n" + "3. Prefer one focused PR per failing test; if several share a root cause, " + "group them and say so.\n" + f"Before starting, search existing sessions/PRs tagged '{tag}' or " + "referencing these test ids and continue that work instead of restarting.\n\n" + "Failing tests and their captured output:\n" + ) + blocks = [f"### {i}. {f.nodeid}\n```\n{f.detail}\n```\n" for i, f in enumerate(shown, start=1)] + return header + task + "\n".join(blocks) + + +def _resolve_tool_name(transport: HttpTransport, cfg: Config) -> str | None: + """Find Devin's create-session tool on the gateway. The proxy prefixes tools + with the server alias, so match by suffix and (when present) the owning + server.""" + result = transport.get( + _LIST_PATH, headers=transport.master, params=_NoParams(), response_type=_McpToolsList + ) + if not isinstance(result, Success): + print(f"bob_the_builder: could not list gateway MCP tools: {result}") + return None + for tool in result.data.tools: + owner = tool.mcp_info.server_name or tool.mcp_info.alias if tool.mcp_info else None + if (owner is None or owner == cfg.server) and ( + tool.name == cfg.create_tool or tool.name.endswith(cfg.create_tool) + ): + return tool.name + print( + f"bob_the_builder: no '{cfg.create_tool}' tool for server '{cfg.server}' on the gateway; " + f"saw {[t.name for t in result.data.tools]}" + ) + return None + + +def remediate(session: pytest.Session) -> None: + """Entry point called from ``pytest_sessionfinish``. No-op unless remediation + is enabled and the run actually had failures.""" + if os.environ.get(REMEDIATION_ENV) != "1": + return + cfg = load_config() + failures = collect_failures(session, cfg.max_detail_chars) + if not failures: + return + + tag = dedup_tag(failures) + title = f"Fix {len(failures)} failing LiteLLM e2e test(s) [{tag}]" + prompt = build_prompt(cfg, failures, tag) + args = _DevinSessionArgs(prompt=prompt, title=title, tags=[*cfg.tags, tag]) + + if cfg.dry_run: + print("bob_the_builder: DRY RUN -- would create a Devin session:") + print(f" server : {cfg.server}\n tool : {cfg.create_tool}\n title : {title}") + print(f" tags : {args.tags}\n---- prompt ----\n{prompt}") + return + + try: + transport = HttpTransport(base_url=PROXY_BASE_URL, master_key=MASTER_KEY) + tool_name = _resolve_tool_name(transport, cfg) + if tool_name is None: + return + result = transport.post( + _CALL_PATH, + headers=transport.master, + json=_ToolCallBody(name=tool_name, arguments=args), + response_type=_ToolCallResult, + ) + if isinstance(result, Success): + print(f"bob_the_builder: created Devin session for {len(failures)} failure(s) [{tag}]") + print(result.data.model_dump_json()) + else: + print(f"bob_the_builder: Devin session call failed: {result}") + except Exception as exc: # noqa: BLE001 - remediation must never fail the run + print(f"bob_the_builder: remediation error (ignored): {exc}") diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 9ca5840df24..82f2604d492 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -107,6 +107,13 @@ def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: if spend_dir in sys.path: sys.path.remove(spend_dir) + try: + from bob_the_builder import remediate + + remediate(session) + except Exception as exc: # noqa: BLE001 - remediation is best-effort + print(f"devin remediation skipped: {exc}") + @pytest.fixture def resources(client: GatewayProvider) -> Iterator[ResourceManager]: diff --git a/tests/e2e/docker-compose.yml b/tests/e2e/docker-compose.yml index cdf5d6cbf6f..195badc5285 100644 --- a/tests/e2e/docker-compose.yml +++ b/tests/e2e/docker-compose.yml @@ -25,6 +25,10 @@ configs: fallbacks: - gemini-2.5-flash: ["gpt-5.5", "claude-haiku-4-5"] + finetune_settings: + - custom_llm_provider: openai + api_key: os.environ/OPENAI_API_KEY + model_list: - model_name: gpt-5.5 litellm_params: diff --git a/tests/e2e/e2e_gateway.py b/tests/e2e/e2e_gateway.py index d62c9c4b17b..05f83ecc085 100644 --- a/tests/e2e/e2e_gateway.py +++ b/tests/e2e/e2e_gateway.py @@ -28,6 +28,9 @@ from models import ( CustomerDeleteBody, EmbedBody, EmbedResponse, + FileListResponse, + FineTuningJobsParams, + FineTuningJobsResponse, KeyDeleteBody, KeyGenerateBody, KeyGenerateResponse, @@ -120,6 +123,24 @@ class Gateway: ) ).data + def list_files(self, key: str) -> Result[FileListResponse]: + return self.transport.get( + "/v1/files", + headers=self.transport.bearer(key), + params=NoBody(), + response_type=FileListResponse, + ) + + def list_fine_tuning_jobs( + self, key: str, params: FineTuningJobsParams + ) -> Result[FineTuningJobsResponse]: + return self.transport.get( + "/v1/fine_tuning/jobs", + headers=self.transport.bearer(key), + params=params, + response_type=FineTuningJobsResponse, + ) + def create_model( self, model_name: str, diff --git a/tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md b/tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md new file mode 100644 index 00000000000..ff8b3441d86 --- /dev/null +++ b/tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md @@ -0,0 +1,66 @@ +# Realtime e2e coverage + +Live tests for the proxy realtime websocket endpoint (`/v1/realtime`). One +GA-speaking websocket client drives every provider; the proxy normalizes each +provider's stream into the OpenAI GA event schema, so the same assertions hold +across providers and only the model alias changes. + +## What is asserted + +For each configured provider, `test_text_conversation` checks the session +lifecycle (`session.created`, then `session.update` echoed by `session.updated`), +the canonical response sequence (`response.created`, `response.output_item.added`, +through `response.done`), that the streamed deltas reconstruct a non-empty +transcript, and that `response.done` carries normalized usage. + +`test_tool_call_round_trip` checks the full tool path: the model emits a +normalized `response.function_call_arguments.done` with valid JSON arguments and +a matching `function_call` output item, the test sends a `function_call_output` +back, and the follow-up response incorporates the result (the temperature 72 +appears). + +`test_realtime_pipecat_e2e` is a realism layer that drives the same providers +through pipecat's GA `OpenAIRealtimeLLMService` (base_url pointed at the proxy) +rather than speaking the protocol by hand. Its assertions are coarse (the tool +callback fired, assistant text was produced); the raw-websocket suite is the +source of truth. It skips unless `pipecat-ai` is installed +(`uv pip install "pipecat-ai[openai]"`). + +## Provisioning + +The suite registers every provider's realtime deployment through `/model/new` at +session start (the `realtime_models` fixture) and deletes them on teardown, so it +never depends on a static or misconfigured gateway `model_list`. Each deployment +is created with `model_info.mode: realtime` and marker-unique names, and its +`litellm_params` point the credentials at `os.environ/*` refs the gateway resolves +at call time. The provider table below is the source of truth; edit `PROVIDERS` in +`realtime_client.py` to change a model or add one. + +| provider | model alias | upstream model | +|----------|-------------|----------------| +| openai | `openai-realtime` | `openai/gpt-realtime-2` | +| azure | `azure-realtime` | `azure/gpt-realtime-2` (GA protocol) | +| gemini | `gemini-realtime` | `gemini/gemini-3.1-flash-live-preview` | +| vertex_ai | `vertex-realtime` | `vertex_ai/gemini-live-2.5-flash-preview-native-audio-09-2025` | + +Bedrock and xai (`xai/grok-4-1-fast-non-reasoning`) are supported by the proxy but +kept commented out in `PROVIDERS` until they pass end-to-end here; re-enable them by +uncommenting their entry. + +Every provider is provisioned and asserted; the suite never skips a provider. Per +`tests/e2e/CLAUDE.md` the only sanctioned skip is the whole-suite proxy-liveness +skip, so a provider whose credentials or upstream realtime model are missing on the +gateway is a hard failure, not a skip. Give the gateway each provider's credentials +to turn its tests green. + +## Running + +Start a proxy with the provider keys set in its environment (the suite registers +the deployments itself), then + +``` +uv run pytest tests/e2e/llm_translation/realtime/ -v +``` + +The whole suite skips only when no proxy answers `GET /health/liveliness` at +`LITELLM_PROXY_URL` (default `http://localhost:4000`). diff --git a/tests/e2e/llm_translation/realtime/conftest.py b/tests/e2e/llm_translation/realtime/conftest.py new file mode 100644 index 00000000000..15cd789664e --- /dev/null +++ b/tests/e2e/llm_translation/realtime/conftest.py @@ -0,0 +1,37 @@ +"""Realtime suite's `client` and `realtime_models` fixtures. + +The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker +live in the parent tests/e2e/conftest.py. RealtimeClient holds the shared Gateway, +so the `resources` fixture cleans up keys this suite creates. + +`realtime_models` registers every provider's realtime deployment through /model/new +at session start and deletes them at teardown, so the suite provisions the models it +uses through the management endpoints instead of depending on a static (or +misconfigured) gateway model_list. +""" + +from collections.abc import Iterator + +import pytest + +from realtime_client import PROVIDERS, RealtimeClient, build_client + + +@pytest.fixture(scope="session") +def client() -> RealtimeClient: + return build_client() + + +@pytest.fixture(scope="session") +def realtime_models(client: RealtimeClient) -> Iterator[dict[str, str]]: + """Provision each provider's realtime deployment via /model/new and yield a + provider-id -> model-name map the tests connect with; delete them on teardown. + Every provider is provisioned (never skipped): a provider whose credentials or + upstream model are missing on the gateway hard-fails its test, per the suite's + fail-on-behavior contract in tests/e2e/CLAUDE.md.""" + records = tuple((provider.id, *client.provision(provider)) for provider in PROVIDERS) + try: + yield {provider_id: model_name for provider_id, model_name, _ in records} + finally: + for _, _, model_id in records: + client.gateway.delete_model(model_id) diff --git a/tests/e2e/realtime/fixtures/weather_question_24k.wav b/tests/e2e/llm_translation/realtime/fixtures/weather_question_24k.wav similarity index 100% rename from tests/e2e/realtime/fixtures/weather_question_24k.wav rename to tests/e2e/llm_translation/realtime/fixtures/weather_question_24k.wav diff --git a/tests/e2e/realtime/pipecat_service.py b/tests/e2e/llm_translation/realtime/pipecat_service.py similarity index 100% rename from tests/e2e/realtime/pipecat_service.py rename to tests/e2e/llm_translation/realtime/pipecat_service.py diff --git a/tests/e2e/realtime/realtime_client.py b/tests/e2e/llm_translation/realtime/realtime_client.py similarity index 69% rename from tests/e2e/realtime/realtime_client.py rename to tests/e2e/llm_translation/realtime/realtime_client.py index 07dfd76108b..ef7834d6bbe 100644 --- a/tests/e2e/realtime/realtime_client.py +++ b/tests/e2e/llm_translation/realtime/realtime_client.py @@ -11,19 +11,19 @@ models, matching the suite's no-raw-dicts rule. from __future__ import annotations import time -from collections.abc import Generator +from collections.abc import Generator, Mapping from contextlib import contextmanager from dataclasses import dataclass from typing import Any, TypeVar from urllib.parse import urlencode -import pytest from pydantic import BaseModel, ConfigDict from websockets.sync.client import connect from websockets.sync.connection import Connection -from e2e_config import PROXY_BASE_URL +from e2e_config import PROXY_BASE_URL, unique_marker from e2e_gateway import Gateway, build_gateway +from models import LiteLLMParamsBody _M = TypeVar("_M", bound=BaseModel) @@ -41,25 +41,76 @@ def realtime_ws_url(model: str) -> str: @dataclass(frozen=True, slots=True) class RealtimeProvider: + """A realtime provider the suite exercises. `litellm_params` is the deployment + the suite registers through /model/new (the gateway resolves the os.environ/* + credential refs), so the suite is self-contained and never depends on a static + gateway model_list. Every provider here is provisioned and asserted: per + tests/e2e/CLAUDE.md the suite never skips a provider, so a provider whose + credentials or upstream realtime model are missing on the gateway is a hard + failure, not a skip.""" + id: str - model: str + alias: str + litellm_params: LiteLLMParamsBody PROVIDERS = ( - RealtimeProvider("openai", "openai-realtime"), - RealtimeProvider("azure", "azure-realtime"), - RealtimeProvider("gemini", "gemini-realtime"), - RealtimeProvider("vertex_ai", "vertex-realtime"), - # RealtimeProvider("bedrock", "bedrock-realtime"), # TODO: Enable this when Bedrock is passing - RealtimeProvider("xai", "xai-realtime"), + RealtimeProvider( + "openai", + "openai-realtime", + LiteLLMParamsBody( + model="openai/gpt-realtime-2", + api_key="os.environ/OPENAI_API_KEY", + ), + ), + RealtimeProvider( + "azure", + "azure-realtime", + LiteLLMParamsBody( + model="azure/gpt-realtime", + api_key="os.environ/AZURE_API_KEY", + api_version="2025-08-28", + realtime_protocol="GA", + ), + ), + RealtimeProvider( + "gemini", + "gemini-realtime", + LiteLLMParamsBody( + model="gemini/gemini-3.1-flash-live-preview", + api_key="os.environ/GEMINI_API_KEY", + ), + ), + RealtimeProvider( + "vertex_ai", + "vertex-realtime", + LiteLLMParamsBody( + model="vertex_ai/gemini-live-2.5-flash-preview-native-audio-09-2025", + vertex_location="us-central1", + vertex_credentials="os.environ/VERTEXAI_CREDENTIALS", + ), + ), + # RealtimeProvider("bedrock", "bedrock-realtime", ...) # TODO: Enable when Bedrock is passing + # RealtimeProvider( + # "xai", + # "xai-realtime", + # LiteLLMParamsBody( + # model="xai/grok-4-1-fast-non-reasoning", + # api_key="os.environ/XAI_API_KEY", + # ), + # ), # TODO: Enable once xai Grok Voice realtime is passing end-to-end here ) -def skip_if_unconfigured( - provider: RealtimeProvider, configured: frozenset[str] -) -> None: - if provider.model not in configured: - pytest.skip(f"{provider.model} not configured on proxy") +def realtime_model(provider: RealtimeProvider, provisioned: Mapping[str, str]) -> str: + """Return the provisioned deployment name for this provider. Every provider in + PROVIDERS is provisioned at session start, so a missing entry is a harness bug, + never an environment skip - the suite hard-fails instead (see tests/e2e/CLAUDE.md).""" + model = provisioned.get(provider.id) + assert model is not None, ( + f"{provider.id} was not provisioned; the realtime_models fixture is broken" + ) + return model # ---- sent events ------------------------------------------------------- @@ -280,12 +331,17 @@ class RealtimeSession: class RealtimeClient: gateway: Gateway - def configured_models(self) -> frozenset[str]: - return frozenset( - entry.model_name - for entry in self.gateway.model_info() - if entry.model_info.mode == "realtime" + def provision(self, provider: RealtimeProvider) -> tuple[str, str]: + """Register this provider's realtime deployment through /model/new and return + (model_name, model_id). The name is marker-unique so it never collides with a + same-named deployment already on the shared proxy, and mode=realtime makes it + show up as a realtime model on /model/info. add_deployment runs synchronously, + so the deployment is connectable as soon as this returns.""" + model_name = f"{provider.alias}-{unique_marker()}" + model_id = self.gateway.create_model( + model_name, provider.litellm_params, mode="realtime" ) + return model_name, model_id @contextmanager def connect( diff --git a/tests/e2e/realtime/test_realtime_e2e.py b/tests/e2e/llm_translation/realtime/test_realtime_e2e.py similarity index 92% rename from tests/e2e/realtime/test_realtime_e2e.py rename to tests/e2e/llm_translation/realtime/test_realtime_e2e.py index 01356900141..6aaffdd208e 100644 --- a/tests/e2e/realtime/test_realtime_e2e.py +++ b/tests/e2e/llm_translation/realtime/test_realtime_e2e.py @@ -31,7 +31,7 @@ from realtime_client import ( SessionUpdate, function_call_item, parse_last, - skip_if_unconfigured, + realtime_model, transcript, user_message, ) @@ -62,12 +62,12 @@ class WeatherResult(BaseModel): def test_text_conversation( client: RealtimeClient, scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - with client.connect(key=scoped_key, model=provider.model) as session: + with client.connect(key=scoped_key, model=model) as session: created = session.collect_until("session.created", timeout=20) assert created[-1].type == "session.created" @@ -99,12 +99,12 @@ def test_text_conversation( def test_tool_call_round_trip( client: RealtimeClient, scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - with client.connect(key=scoped_key, model=provider.model) as session: + with client.connect(key=scoped_key, model=model) as session: session.collect_until("session.created", timeout=20) session.send( SessionUpdate( diff --git a/tests/e2e/realtime/test_realtime_pipecat_audio_e2e.py b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e.py similarity index 95% rename from tests/e2e/realtime/test_realtime_pipecat_audio_e2e.py rename to tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e.py index d9d7c744f66..31c038b4e02 100644 --- a/tests/e2e/realtime/test_realtime_pipecat_audio_e2e.py +++ b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e.py @@ -31,7 +31,7 @@ from realtime_client import ( PROVIDERS, RealtimeProvider, _ws_base_url, - skip_if_unconfigured, + realtime_model, ) pytestmark = pytest.mark.e2e @@ -189,13 +189,13 @@ async def _run_pipeline( @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_server_vad( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: """Session is configured with server-VAD; bot must respond to a text prompt.""" - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - tool_called, got_text, _ = asyncio.run(_run_pipeline(scoped_key, provider.model)) + tool_called, got_text, _ = asyncio.run(_run_pipeline(scoped_key, model)) assert tool_called, "get_weather tool was not invoked" assert got_text, "no assistant text frames produced" @@ -204,16 +204,16 @@ def test_pipecat_server_vad( @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_audio_output( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: """Bot must produce at least one non-empty TTS audio frame.""" - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) _, got_text, audio_bytes = asyncio.run( _run_pipeline( scoped_key, - provider.model, + model, prompt="Say hello in one short sentence.", timeout=30.0, ) @@ -328,7 +328,7 @@ async def _run_audio_input_pipeline( @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_server_vad_audio_input( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: """Stream a real PCM16 WAV fixture; server VAD must detect speech end and respond. @@ -337,12 +337,11 @@ def test_pipecat_server_vad_audio_input( → server-VAD turn detection → response.create (auto) → assistant reply. No LLMRunFrame is sent — the response must be triggered entirely by VAD. """ - if not WEATHER_WAV.exists(): - pytest.skip(f"audio fixture not found: {WEATHER_WAV}") - skip_if_unconfigured(provider, configured_models) + assert WEATHER_WAV.exists(), f"audio fixture not found: {WEATHER_WAV}" + model = realtime_model(provider, realtime_models) got_text, audio_bytes = asyncio.run( - _run_audio_input_pipeline(scoped_key, provider.model) + _run_audio_input_pipeline(scoped_key, model) ) assert got_text, "server VAD did not trigger a response (no assistant text)" diff --git a/tests/e2e/realtime/test_realtime_pipecat_e2e.py b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_e2e.py similarity index 97% rename from tests/e2e/realtime/test_realtime_pipecat_e2e.py rename to tests/e2e/llm_translation/realtime/test_realtime_pipecat_e2e.py index 1068c54fdec..799958ef4e3 100644 --- a/tests/e2e/realtime/test_realtime_pipecat_e2e.py +++ b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_e2e.py @@ -29,7 +29,7 @@ from realtime_client import ( PROVIDERS, RealtimeProvider, _ws_base_url, - skip_if_unconfigured, + realtime_model, ) pytestmark = pytest.mark.e2e @@ -123,12 +123,12 @@ async def _run_pipeline(key: str, model: str) -> tuple[bool, bool]: @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_tool_smoke( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - tool_called, produced_text = asyncio.run(_run_pipeline(scoped_key, provider.model)) + tool_called, produced_text = asyncio.run(_run_pipeline(scoped_key, model)) assert tool_called, "pipecat did not invoke the get_weather callback" assert produced_text, "pipecat produced no assistant text frames" diff --git a/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py b/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py index f8f229aa2a7..5adb8c24f9f 100644 --- a/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py +++ b/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py @@ -2,17 +2,14 @@ DeepSeek's reasoner defaults thinking ON and surfaces the chain as ``message.reasoning_content``. Two documented ways to disable it are -``reasoning_effort="none"`` and ``thinking={"type": "disabled"}``. Today the -DeepSeek param mapper (``litellm/llms/deepseek/chat/transformation.py`` -``map_openai_params``) drops both without forwarding any disable signal, so the -outbound body carries no ``thinking`` key and DeepSeek keeps thinking on; the -response still comes back with ``reasoning_content``. That is the product gap -tracked by LIT-3686 / GH #27453. +``reasoning_effort="none"`` and ``thinking={"type": "disabled"}``. The DeepSeek +param mapper (``litellm/llms/deepseek/chat/transformation.py`` +``map_openai_params``) forwards both as ``thinking={"type": "disabled"}`` so the +outbound body carries a real disable signal and ``deepseek-reasoner`` returns no +``reasoning_content``. This is the behavior tracked by LIT-3686 / GH #27453. The control case proves the model and path work (reasoning is returned when -nothing asks to disable it), so the two disable assertions are meaningful. Those -two are marked xfail(strict) until the mapper forwards a real disable signal; an -xpass then alerts that the fix landed. +nothing asks to disable it), so the two disable assertions are meaningful. Requires DEEPSEEK_API_KEY on the proxy (tests/e2e/.env). No skip gate: once the proxy is up, a failure here is real, per the suite's hard-fail contract. @@ -74,13 +71,6 @@ class TestDeepSeekReasoningDisable: f"disable param, so the disable assertions below can't be trusted: {response}" ) - @pytest.mark.xfail( - strict=True, - reason=( - "LIT-3686 / GH #27453: DeepSeek reasoning_effort='none' and " - "thinking type='disabled' are silently dropped; reasoning not disabled" - ), - ) def test_reasoning_effort_none_disables_reasoning( self, client: PassthroughClient, resources: ResourceManager ) -> None: @@ -103,13 +93,6 @@ class TestDeepSeekReasoningDisable: f"is still present: {response}" ) - @pytest.mark.xfail( - strict=True, - reason=( - "LIT-3686 / GH #27453: DeepSeek reasoning_effort='none' and " - "thinking type='disabled' are silently dropped; reasoning not disabled" - ), - ) def test_thinking_disabled_disables_reasoning( self, client: PassthroughClient, resources: ResourceManager ) -> None: diff --git a/tests/e2e/llm_translation/test_ocr_rust_e2e.py b/tests/e2e/llm_translation/test_ocr_rust_e2e.py index 361bb5126a7..921010e5eae 100644 --- a/tests/e2e/llm_translation/test_ocr_rust_e2e.py +++ b/tests/e2e/llm_translation/test_ocr_rust_e2e.py @@ -86,16 +86,18 @@ class AzureDocIntelligenceOcr: @dataclass(frozen=True, slots=True) class VertexOcr: + """Vertex AI OCR (Mistral publisher). Only the location (not a secret) is set; + the project and credentials are left unset so the gateway resolves VERTEXAI_PROJECT + and VERTEXAI_CREDENTIALS from its own environment by name, keeping every secret on + the gateway like the azure_ai cases above. This is deliberate: the OCR path reads + vertex_project verbatim from litellm_params and never unwraps an `os.environ/*` + ref, so passing one would put the literal string in the request URL.""" + model: str location: str def litellm_params(self) -> LiteLLMParamsBody: - return LiteLLMParamsBody( - model=self.model, - vertex_project="os.environ/VERTEXAI_PROJECT", - vertex_location=self.location, - vertex_credentials="os.environ/VERTEXAI_CREDENTIALS", - ) + return LiteLLMParamsBody(model=self.model, vertex_location=self.location) @dataclass(frozen=True, slots=True) @@ -113,7 +115,7 @@ RUST_OCR_CASES: tuple[_OcrCase, ...] = ( ), _OcrCase( "azure-ai", - AzureAiOcr("azure_ai/mistral-document-ai-2505"), + AzureAiOcr("azure_ai/mistral-document-ai-2512"), OcrDocument(type="document_url", document_url=TEST_PDF_URL), ), _OcrCase( @@ -126,11 +128,6 @@ RUST_OCR_CASES: tuple[_OcrCase, ...] = ( VertexOcr("vertex_ai/mistral-ocr-2505", "us-central1"), OcrDocument(type="document_url", document_url=TEST_PDF_URL), ), - _OcrCase( - "vertex-deepseek", - VertexOcr("vertex_ai/deepseek-ocr-maas", "global"), - OcrDocument(type="image_url", image_url=TEST_IMAGE_URL), - ), ) _CASE_IDS = tuple(case.suffix for case in RUST_OCR_CASES) @@ -155,3 +152,5 @@ class TestRustOcrGateway: response = unwrap(endpoints_client.gateway.ocr(key, OcrBody(model=model, document=case.document))) _assert_ocr_document(response) + + diff --git a/tests/e2e/llm_translation/test_provider_features_e2e.py b/tests/e2e/llm_translation/test_provider_features_e2e.py index cf05a4306b4..d272fffa9b8 100644 --- a/tests/e2e/llm_translation/test_provider_features_e2e.py +++ b/tests/e2e/llm_translation/test_provider_features_e2e.py @@ -3,11 +3,13 @@ Each case asserts the feature took effect, not just a 200. service_tier is an OpenAI concept. The proxy forwards it and the provider echoes -the tier back on the response, so sending a non-default tier ("flex") and reading -it back off ``service_tier`` proves the param was honored end to end; litellm's own -default injection would report "default", so a "flex" echo can only come from the -request being forwarded. Bedrock and Vertex do not accept service_tier, so that -cell is OpenAI-only by design. +the tier back on the response, so sending a non-default tier ("priority") and +reading it back off ``service_tier`` proves the param was honored end to end; +litellm's own default injection (and service_tier="auto") both report "default", +so a "priority" echo can only come from the request being forwarded. "flex" is +avoided here because it is capacity-constrained and returns a transient 429 when +flex resources are unavailable. Bedrock and Vertex do not accept service_tier, so +that cell is OpenAI-only by design. Prompt caching is asserted through provider prompt-cache usage tokens. The deterministic path is explicit ``cache_control`` on an Anthropic-family model @@ -22,7 +24,7 @@ scope here and covered only by the explicit-cache-control Bedrock case. from __future__ import annotations import pytest -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict, Field from e2e_config import unique_marker from e2e_http import unwrap @@ -32,7 +34,7 @@ from passthrough_client import PassthroughClient pytestmark = pytest.mark.e2e -SERVICE_TIER = "flex" +SERVICE_TIER = "priority" CACHE_MIN_READ_TOKENS = 1 @@ -51,10 +53,21 @@ class RichMessage(BaseModel): content: list[CacheTextBlock] +class CacheDirective(BaseModel): + """litellm per-request cache control. ``no-cache`` forces the proxy to skip its + own response cache and make a fresh provider call, so the second identical + request actually reaches Bedrock and reads the provider prompt cache instead of + being served the first response verbatim (which would report cache_read=0).""" + + model_config = ConfigDict(populate_by_name=True) + no_cache: bool = Field(default=True, alias="no-cache") + + class CacheChatBody(BaseModel): model: str messages: list[RichMessage] max_tokens: int + cache: CacheDirective = CacheDirective() def cacheable_prefix() -> str: diff --git a/tests/e2e/models.py b/tests/e2e/models.py index f287058b313..38778034de9 100644 --- a/tests/e2e/models.py +++ b/tests/e2e/models.py @@ -376,6 +376,7 @@ class LiteLLMParamsBody(BaseModel): api_key: str | None = None api_base: str | None = None api_version: str | None = None + realtime_protocol: str | None = None aws_region_name: str | None = None vertex_project: str | None = None vertex_location: str | None = None diff --git a/tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md b/tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md deleted file mode 100644 index 8624475d0de..00000000000 --- a/tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md +++ /dev/null @@ -1,55 +0,0 @@ -# Realtime e2e coverage - -Live tests for the proxy realtime websocket endpoint (`/v1/realtime`). One -GA-speaking websocket client drives every provider; the proxy normalizes each -provider's stream into the OpenAI GA event schema, so the same assertions hold -across providers and only the model alias changes. - -## What is asserted - -For each configured provider, `test_text_conversation` checks the session -lifecycle (`session.created`, then `session.update` echoed by `session.updated`), -the canonical response sequence (`response.created`, `response.output_item.added`, -through `response.done`), that the streamed deltas reconstruct a non-empty -transcript, and that `response.done` carries normalized usage. - -`test_tool_call_round_trip` checks the full tool path: the model emits a -normalized `response.function_call_arguments.done` with valid JSON arguments and -a matching `function_call` output item, the test sends a `function_call_output` -back, and the follow-up response incorporates the result (the temperature 72 -appears). - -`test_realtime_pipecat_e2e` is a realism layer that drives the same providers -through pipecat's GA `OpenAIRealtimeLLMService` (base_url pointed at the proxy) -rather than speaking the protocol by hand. Its assertions are coarse (the tool -callback fired, assistant text was produced); the raw-websocket suite is the -source of truth. It skips unless `pipecat-ai` is installed -(`uv pip install "pipecat-ai[openai]"`). - -## Provider status - -| provider | model alias | status | -|----------|-------------|--------| -| openai | `openai-realtime` | covered (in gateway config) | -| gemini | `gemini-realtime` | covered (in gateway config; needs Gemini Live API access) | -| azure | `azure-realtime` | gap: add to gateway config + AZURE creds | -| vertex_ai | `vertex-realtime` | gap: add to gateway config + Vertex creds | -| bedrock | `bedrock-realtime` | gap: add to gateway config + AWS creds | -| xai | `xai-realtime` | gap: add to gateway config + XAI_API_KEY | - -A provider whose alias is not present in the proxy's `/model/info` skips (skip on -environment). To enable one, add a `model_info.mode: realtime` entry under that -alias to `tests/e2e/gateway/litellm-config.yml` and give the proxy the -provider's credentials; the test then runs with no code change. - -## Running - -Start a proxy with the gateway config and the provider keys set in its -environment, then - -``` -uv run pytest tests/e2e/realtime/ -v -``` - -Tests skip when no proxy answers `GET /health/liveliness` at `LITELLM_PROXY_URL` -(default `http://localhost:4000`). diff --git a/tests/e2e/realtime/conftest.py b/tests/e2e/realtime/conftest.py deleted file mode 100644 index 4a5c4837a1a..00000000000 --- a/tests/e2e/realtime/conftest.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Realtime suite's `client` fixture. - -The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker -live in the parent tests/e2e/conftest.py. RealtimeClient holds the shared -Gateway, so the `resources` fixture cleans up keys this suite creates. -""" - -import pytest - -from realtime_client import RealtimeClient, build_client - - -@pytest.fixture(scope="session") -def client() -> RealtimeClient: - return build_client() - - -@pytest.fixture(scope="session") -def configured_models(client: RealtimeClient) -> frozenset[str]: - return client.configured_models() diff --git a/tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py b/tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py new file mode 100644 index 00000000000..0c8b1cc2836 --- /dev/null +++ b/tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py @@ -0,0 +1,85 @@ +""" +Regression tests for Azure Document Intelligence api_base resolution in OCR. + +`azure_ai` exposes two OCR services on one provider; the `doc-intelligence` +sub-route must resolve to `AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT`, not to the +generic `AZURE_AI_API_BASE` fallback that `get_llm_provider` injects. These tests +pin that routing and guard the backwards-compatibility contract that an explicitly +supplied api_base is always honoured. +""" + +from litellm.llms.azure_ai.ocr.common_utils import ( + is_azure_document_intelligence_model, +) +from litellm.ocr.main import _prepare_ocr_request, _rust_bridge_api_base + +_DOC = {"type": "document_url", "document_url": "https://example.com/doc.pdf"} +_DOC_INTELLIGENCE_ENDPOINT = "https://di.cognitiveservices.azure.com" +_AZURE_AI_API_BASE = "https://generic-azure-ai.example.com" + + +class _FakeLogging: + def update_from_kwargs(self, **kwargs: object) -> None: + return None + + +def _resolve_secret(name: str) -> str | None: + return { + "AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT": _DOC_INTELLIGENCE_ENDPOINT, + "AZURE_AI_API_BASE": _AZURE_AI_API_BASE, + }.get(name) + + +def _prepare(model: str, api_base: str | None): + return _prepare_ocr_request( + model=model, + document=dict(_DOC), + api_key="test-key", + api_base=api_base, + timeout=None, + custom_llm_provider=None, + extra_headers=None, + kwargs={"litellm_logging_obj": _FakeLogging()}, + ) + + +class TestIsAzureDocumentIntelligenceModel: + def test_matches_doc_intelligence_route(self): + assert is_azure_document_intelligence_model("doc-intelligence/prebuilt-layout") + + def test_matches_documentintelligence_and_is_case_insensitive(self): + assert is_azure_document_intelligence_model("azure_ai/DocumentIntelligence/x") + + def test_does_not_match_mistral_route(self): + assert not is_azure_document_intelligence_model("mistral-document-ai-2505") + + +class TestDocIntelligenceApiBaseResolution: + def test_generic_azure_ai_base_does_not_hijack_doc_intelligence(self, monkeypatch): + """Without an explicit api_base, the AZURE_AI_API_BASE fallback must not + overwrite the endpoint, so it resolves to the Document Intelligence one.""" + monkeypatch.setenv("AZURE_AI_API_BASE", _AZURE_AI_API_BASE) + monkeypatch.delenv("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT", raising=False) + + prepared = _prepare("azure_ai/doc-intelligence/prebuilt-layout", None) + + assert prepared.api_base is None + assert _rust_bridge_api_base(prepared, _resolve_secret) == _DOC_INTELLIGENCE_ENDPOINT + + def test_explicit_api_base_is_honoured_for_doc_intelligence(self, monkeypatch): + """A caller-supplied api_base must always win, even for doc-intelligence.""" + monkeypatch.setenv("AZURE_AI_API_BASE", _AZURE_AI_API_BASE) + + custom = "https://my-di.cognitiveservices.azure.com" + prepared = _prepare("azure_ai/doc-intelligence/prebuilt-layout", custom) + + assert prepared.api_base == custom + assert _rust_bridge_api_base(prepared, _resolve_secret) == custom + + def test_generic_azure_ai_base_still_applies_to_mistral_ocr(self, monkeypatch): + """Non doc-intelligence azure_ai models keep using AZURE_AI_API_BASE.""" + monkeypatch.setenv("AZURE_AI_API_BASE", _AZURE_AI_API_BASE) + + prepared = _prepare("azure_ai/mistral-document-ai-2505", None) + + assert prepared.api_base == _AZURE_AI_API_BASE From 41e9cc491ed08a70b96ba1f77efb39de76680943 Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:31:27 -0700 Subject: [PATCH 073/399] fix(bedrock-invoke): retain clear_tool_uses_20250919 context_management edits and emit context-management-2025-06-27 beta (LIT-3393) (#32658) * fix(bedrock-invoke): retain clear_tool_uses_20250919 context_management edits and emit context-management-2025-06-27 beta (LIT-3393) Copy of #29206 by oss-agent-shin, rebased onto litellm_internal_staging so CircleCI can run. Bedrock InvokeModel supports automatic tool-call clearing (clear_tool_uses_20250919) under the context-management-2025-06-27 beta, but LiteLLM stripped the edit and dropped the beta header, causing a Bedrock 400. This maps bedrock.context-management-2025-06-27 to itself in anthropic_beta_headers_config.json (bedrock_converse stays null) and rewrites _filter_context_management_for_bedrock_invoke around an allowlist of supported edit types that keeps each supported edit and adds its matching beta. * test(bedrock-invoke): restore beta-headers config cache with a shared fixture in LIT-3393 tests Greptile flagged that three of the four new tests reloaded the module-level beta-headers config into local mode without restoring it on teardown, leaking state into later tests in the same process. Move setup/teardown into a local_beta_headers_config fixture used by all four tests. --------- Co-authored-by: oss-agent-shin --- litellm/anthropic_beta_headers_config.json | 2 +- .../anthropic_claude3_transformation.py | 57 ++++-- .../test_anthropic_claude3_transformation.py | 171 ++++++++++++++++++ 3 files changed, 211 insertions(+), 19 deletions(-) diff --git a/litellm/anthropic_beta_headers_config.json b/litellm/anthropic_beta_headers_config.json index 11fdb26e42d..3f6817f6e35 100644 --- a/litellm/anthropic_beta_headers_config.json +++ b/litellm/anthropic_beta_headers_config.json @@ -102,7 +102,7 @@ "computer-use-2025-01-24": "computer-use-2025-01-24", "computer-use-2025-11-24": "computer-use-2025-11-24", "context-1m-2025-08-07": "context-1m-2025-08-07", - "context-management-2025-06-27": null, + "context-management-2025-06-27": "context-management-2025-06-27", "effort-2025-11-24": "effort-2025-11-24", "fast-mode-2026-02-01": null, "files-api-2025-04-14": null, diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index f5309d521a9..daee3369a3c 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -489,24 +489,43 @@ class AmazonAnthropicClaudeMessagesConfig( if self._supports_tool_search_on_bedrock(model): beta_set.add("tool-search-tool-2025-10-19") + # Bedrock-InvokeModel-supported ``context_management.edits`` types and the + # ``anthropic-beta`` header that each one requires. ``clear_thinking_20251015`` + # is intentionally absent — it is LiteLLM-internal, consumed via + # ``_ensure_thinking_for_clear_thinking_context_management``, and forwarding + # the raw edit trips Bedrock's + # ``"context_management: Extra inputs are not permitted"`` 400. + # + # Bedrock InvokeModel DOES support ``clear_tool_uses_20250919`` under the + # ``context-management-2025-06-27`` beta. AWS docs: + # https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md + _BEDROCK_INVOKE_SUPPORTED_CONTEXT_MANAGEMENT_EDITS: Dict[str, str] = { + "compact_20260112": ANTHROPIC_BETA_HEADER_VALUES.COMPACT_2026_01_12.value, + "clear_tool_uses_20250919": ANTHROPIC_BETA_HEADER_VALUES.CONTEXT_MANAGEMENT_2025_06_27.value, + } + @staticmethod def _filter_context_management_for_bedrock_invoke( anthropic_messages_request: Dict, beta_set: set, ) -> None: """ - Bedrock InvokeModel accepts ``context_management`` only when it carries - ``compact_20260112`` edits paired with the ``compact-2026-01-12`` - anthropic-beta header. Other edit types (notably ``clear_thinking_20251015``, - which Claude Code sends on every request) are LiteLLM-internal and would - cause Bedrock to 400 with ``"context_management: Extra inputs are not - permitted"``. + Filter ``context_management.edits`` to the subset that Bedrock InvokeModel + accepts and add the matching ``anthropic-beta`` header for each surviving + edit type. - Filter the edits list to the supported subset, add the beta header when - compact edits remain, and drop ``context_management`` entirely when no - supported edits are left so the safety-net allowlist can pass it through. + - ``compact_20260112`` -> ``compact-2026-01-12`` + - ``clear_tool_uses_20250919`` -> ``context-management-2025-06-27`` - Ref: https://github.com/BerriAI/litellm/issues/27532 + Other edit types (notably ``clear_thinking_20251015``, which Claude Code + sends on every request) are LiteLLM-internal: thinking is injected + separately via ``_ensure_thinking_for_clear_thinking_context_management``, + and forwarding the raw edit would trip Bedrock's + ``"context_management: Extra inputs are not permitted"`` 400. + + Refs: + * https://github.com/BerriAI/litellm/issues/27532 + * https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md """ cm = anthropic_messages_request.get("context_management") if not isinstance(cm, dict): @@ -516,15 +535,17 @@ class AmazonAnthropicClaudeMessagesConfig( anthropic_messages_request.pop("context_management", None) return - compact_edits = [e for e in edits if isinstance(e, dict) and e.get("type") == "compact_20260112"] - if compact_edits: - beta_set.add(ANTHROPIC_BETA_HEADER_VALUES.COMPACT_2026_01_12.value) - anthropic_messages_request["context_management"] = { - **cm, - "edits": compact_edits, - } - else: + supported = AmazonAnthropicClaudeMessagesConfig._BEDROCK_INVOKE_SUPPORTED_CONTEXT_MANAGEMENT_EDITS + retained_edits = [e for e in edits if isinstance(e, dict) and e.get("type") in supported] + if not retained_edits: anthropic_messages_request.pop("context_management", None) + return + + beta_set.update(supported[e["type"]] for e in retained_edits) + anthropic_messages_request["context_management"] = { + **cm, + "edits": retained_edits, + } def _get_bedrock_invoke_anthropic_beta_headers( self, diff --git a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py index d7a62aae38b..532c6ff3598 100644 --- a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py +++ b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py @@ -2090,3 +2090,174 @@ def test_bedrock_clear_thinking_leaves_enabled_thinking_on_non_adaptive_model(): assert changed is False assert request["thinking"] == {"type": "enabled", "budget_tokens": 8000} assert "output_config" not in request + + +@pytest.fixture +def local_beta_headers_config(monkeypatch): + from litellm.anthropic_beta_headers_manager import reload_beta_headers_config + + monkeypatch.setenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", "True") + reload_beta_headers_config() + yield + monkeypatch.delenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", raising=False) + reload_beta_headers_config() + + +def test_bedrock_messages_preserves_clear_tool_uses_context_management_and_adds_beta( + local_beta_headers_config, +): + """ + LIT-3393: Bedrock InvokeModel supports automatic tool-call clearing via + ``clear_tool_uses_20250919`` under the ``context-management-2025-06-27`` + beta. Before the LIT-3393 fix, the transformation stripped this edit (only + ``compact_20260112`` survived) AND the beta was filtered out by + ``filter_and_transform_beta_headers`` for ``bedrock``, producing a Bedrock + 400 ``"context_management: Extra inputs are not permitted"``. + + Post-fix, the edit must reach the body and the beta must reach + ``anthropic_beta``. + + AWS docs ("Automatic tool call clearing (Beta)"): + https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md + """ + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}] + optional_params = { + "max_tokens": 4096, + "context_management": { + "edits": [{"type": "clear_tool_uses_20250919"}] + }, + } + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-haiku-4-5-20251001-v1:0", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result.get("context_management") == { + "edits": [{"type": "clear_tool_uses_20250919"}] + }, "clear_tool_uses_20250919 edit must reach Bedrock InvokeModel body" + assert "context-management-2025-06-27" in result.get("anthropic_beta", []), ( + "context-management-2025-06-27 beta must reach the InvokeModel body so " + "the tool-call-clearing edit is accepted" + ) + + +def test_bedrock_messages_preserves_mixed_compact_and_clear_tool_uses_edits( + local_beta_headers_config, +): + """ + LIT-3393: a request mixing ``compact_20260112`` and + ``clear_tool_uses_20250919`` must keep BOTH edits and emit BOTH + anthropic-beta values (``compact-2026-01-12`` + ``context-management-2025-06-27``). + """ + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}] + optional_params = { + "max_tokens": 4096, + "context_management": { + "edits": [ + {"type": "compact_20260112"}, + {"type": "clear_tool_uses_20250919"}, + ] + }, + } + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-sonnet-4-6-20250929-v1:0", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + cm = result.get("context_management") + assert cm is not None + edit_types = sorted(e.get("type") for e in cm["edits"]) + assert edit_types == ["clear_tool_uses_20250919", "compact_20260112"] + + betas = result.get("anthropic_beta", []) + assert "compact-2026-01-12" in betas + assert "context-management-2025-06-27" in betas + + +def test_bedrock_messages_filters_clear_thinking_keeps_clear_tool_uses( + local_beta_headers_config, +): + """ + LIT-3393: ``clear_thinking_20251015`` remains LiteLLM-internal (consumed via + thinking-injection) and MUST be stripped from the body, while + ``clear_tool_uses_20250919`` (officially supported on Bedrock InvokeModel) + survives in the same request. + """ + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}] + optional_params = { + "max_tokens": 4096, + "context_management": { + "edits": [ + {"type": "clear_thinking_20251015", "keep": "all"}, + {"type": "clear_tool_uses_20250919"}, + ] + }, + } + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-opus-4-7", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + cm = result.get("context_management") + assert cm is not None + assert [e.get("type") for e in cm["edits"]] == [ + "clear_tool_uses_20250919" + ], "clear_thinking_20251015 must still be stripped (LiteLLM-internal)" + + betas = result.get("anthropic_beta", []) + assert "context-management-2025-06-27" in betas + # ``compact-2026-01-12`` was not requested. + assert "compact-2026-01-12" not in betas + + +def test_filter_and_transform_beta_headers_passes_context_management_for_bedrock( + local_beta_headers_config, +): + """ + LIT-3393: ``anthropic_beta_headers_config.json`` previously mapped + ``bedrock.context-management-2025-06-27`` to ``null``, so + ``filter_and_transform_beta_headers`` dropped the header even when the + transformation tried to set it. This regression guard locks the bundled + mapping in place. + + Pinned to the bundled local config via ``LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS`` + so the assertion is not subject to whatever the upstream remote currently + serves or what previous tests left in the module cache. + """ + from litellm.anthropic_beta_headers_manager import filter_and_transform_beta_headers + + out = filter_and_transform_beta_headers( + ["context-management-2025-06-27"], + provider="bedrock", + ) + assert out == ["context-management-2025-06-27"] + + # Bedrock_converse genuinely lacks it per AWS docs; this guard prevents + # an accidental flip there. + out_converse = filter_and_transform_beta_headers( + ["context-management-2025-06-27"], + provider="bedrock_converse", + ) + assert out_converse == [] + From 1fa200123fa54f5012fcfe46f8a1a9bd8365e58a Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:37:49 -0700 Subject: [PATCH 074/399] fix(tests): stop DATABASE_URL env pollution from read-replica tests breaking DB e2e tests (#32653) --- tests/test_litellm/proxy/db/conftest.py | 65 +++++++++++++++++++ .../proxy/db/test_db_url_settings.py | 26 ++++---- .../proxy/db/test_rds_iam_token_expiry.py | 44 ++++--------- .../proxy/db/test_routing_prisma_wrapper.py | 6 +- 4 files changed, 89 insertions(+), 52 deletions(-) create mode 100644 tests/test_litellm/proxy/db/conftest.py diff --git a/tests/test_litellm/proxy/db/conftest.py b/tests/test_litellm/proxy/db/conftest.py new file mode 100644 index 00000000000..a0fb6bed4fa --- /dev/null +++ b/tests/test_litellm/proxy/db/conftest.py @@ -0,0 +1,65 @@ +import os +from collections.abc import Generator +from typing import Optional + +import pytest + +DB_ENV_KEYS = ( + "IAM_TOKEN_DB_AUTH", + "DATABASE_URL", + "DIRECT_URL", + "DATABASE_URL_READ_REPLICA", + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_USER", + "DATABASE_USERNAME", + "DATABASE_NAME", + "DATABASE_SCHEMA", + "DATABASE_PASSWORD", + "DATABASE_HOST_READ_REPLICA", + "DATABASE_PORT_READ_REPLICA", + "DATABASE_USER_READ_REPLICA", + "DATABASE_USERNAME_READ_REPLICA", + "DATABASE_NAME_READ_REPLICA", + "DATABASE_SCHEMA_READ_REPLICA", + "DATABASE_PASSWORD_READ_REPLICA", +) + +_db_env_snapshot_key = pytest.StashKey[dict[str, Optional[str]]]() + + +def _db_env_snapshot() -> dict[str, Optional[str]]: + return {key: os.environ.get(key) for key in DB_ENV_KEYS} + + +@pytest.hookimpl(wrapper=True) +def pytest_runtest_setup(item: pytest.Item) -> Generator[None, None, None]: + item.stash[_db_env_snapshot_key] = _db_env_snapshot() + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_runtest_teardown(item: pytest.Item, nextitem: Optional[pytest.Item]) -> Generator[None, None, None]: + result = yield + before = item.stash[_db_env_snapshot_key] + leaked = {key: value for key, value in _db_env_snapshot().items() if value != before[key]} + for key, original in before.items(): + if original is None: + os.environ.pop(key, None) + else: + os.environ[key] = original + assert not leaked, ( + f"{item.nodeid} leaked DB env vars past monkeypatch teardown: {leaked}. " + "Product code under test writes DATABASE_URL(_READ_REPLICA) into os.environ as a side effect; " + "monkeypatch only restores keys it has a record for, so a value written to a previously unset " + "key survives the test and poisons every later test in this pytest-xdist worker process " + "(DB-backed e2e tests arm themselves on DATABASE_URL and then fail to connect). " + "Use the unset_database_url fixture (or monkeypatch.setenv) so restoration is registered." + ) + return result + + +@pytest.fixture +def unset_database_url(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("DATABASE_URL", "about-to-be-unset") + monkeypatch.delenv("DATABASE_URL") diff --git a/tests/test_litellm/proxy/db/test_db_url_settings.py b/tests/test_litellm/proxy/db/test_db_url_settings.py index 573bd5ae584..e5aa09addab 100644 --- a/tests/test_litellm/proxy/db/test_db_url_settings.py +++ b/tests/test_litellm/proxy/db/test_db_url_settings.py @@ -51,25 +51,21 @@ _MANAGED_DB_ENV_VARS = ( @pytest.fixture(autouse=True) -def _scrub_db_env(): +def _scrub_db_env(monkeypatch): """Start each test from a clean slate and restore the original env afterward. - ``apply_to_env`` writes ``DATABASE_URL`` straight into ``os.environ``, which - ``monkeypatch`` cannot undo. Snapshotting and restoring here keeps a - synthesized URL (e.g. ``writer.example.com``) from leaking into later tests - that read ``DATABASE_URL`` to decide whether to hit a real database. + ``apply_to_env`` writes ``DATABASE_URL`` straight into ``os.environ``. + Registering a setenv+delenv pair per var gives ``monkeypatch`` a restore + record even for previously unset keys, so a synthesized URL (e.g. + ``writer.example.com``) cannot leak into later tests that read + ``DATABASE_URL`` to decide whether to hit a real database. Restoring via + the same ``monkeypatch`` instance the tests use also keeps undo ordering + consistent (a hand-rolled snapshot/restore runs before ``monkeypatch``'s + own undo and gets clobbered by it). """ - saved = {var: os.environ.get(var) for var in _MANAGED_DB_ENV_VARS} for var in _MANAGED_DB_ENV_VARS: - os.environ.pop(var, None) - try: - yield - finally: - for var, value in saved.items(): - if value is None: - os.environ.pop(var, None) - else: - os.environ[var] = value + monkeypatch.setenv(var, "scrubbed") + monkeypatch.delenv(var) def _stub_iam_token(token: str = "FAKE_TOKEN"): diff --git a/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py b/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py index b92fd86ed7a..ca24f856022 100644 --- a/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py +++ b/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py @@ -26,25 +26,13 @@ class TestPrismaWrapperTokenRefresh: """Tests for the PrismaWrapper RDS IAM token refresh implementation.""" @pytest.fixture - def setup_env(self): + def setup_env(self, monkeypatch, unset_database_url): """Setup environment variables for testing.""" - os.environ["DATABASE_HOST"] = "test-host.rds.amazonaws.com" - os.environ["DATABASE_PORT"] = "5432" - os.environ["DATABASE_USER"] = "test_user" - os.environ["DATABASE_NAME"] = "test_db" - os.environ["IAM_TOKEN_DB_AUTH"] = "True" - yield - # Cleanup - for key in [ - "DATABASE_HOST", - "DATABASE_PORT", - "DATABASE_USER", - "DATABASE_NAME", - "DATABASE_URL", - "IAM_TOKEN_DB_AUTH", - "DATABASE_SCHEMA", - ]: - os.environ.pop(key, None) + monkeypatch.setenv("DATABASE_HOST", "test-host.rds.amazonaws.com") + monkeypatch.setenv("DATABASE_PORT", "5432") + monkeypatch.setenv("DATABASE_USER", "test_user") + monkeypatch.setenv("DATABASE_NAME", "test_db") + monkeypatch.setenv("IAM_TOKEN_DB_AUTH", "True") def _generate_mock_token(self, expires_in_seconds: int = 900) -> str: """Generate a mock IAM token with expiration info.""" @@ -172,22 +160,12 @@ class TestBackgroundRefreshLoop: """Tests for the background refresh loop timing.""" @pytest.fixture - def setup_env(self): + def setup_env(self, monkeypatch, unset_database_url): """Setup environment variables for testing.""" - os.environ["DATABASE_HOST"] = "test-host.rds.amazonaws.com" - os.environ["DATABASE_PORT"] = "5432" - os.environ["DATABASE_USER"] = "test_user" - os.environ["DATABASE_NAME"] = "test_db" - yield - # Cleanup - for key in [ - "DATABASE_HOST", - "DATABASE_PORT", - "DATABASE_USER", - "DATABASE_NAME", - "DATABASE_URL", - ]: - os.environ.pop(key, None) + monkeypatch.setenv("DATABASE_HOST", "test-host.rds.amazonaws.com") + monkeypatch.setenv("DATABASE_PORT", "5432") + monkeypatch.setenv("DATABASE_USER", "test_user") + monkeypatch.setenv("DATABASE_NAME", "test_db") @pytest.mark.asyncio async def test_calculate_seconds_fallback_when_no_url(self, setup_env): diff --git a/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py b/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py index 92043f44ca9..e5bb8b99507 100644 --- a/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py +++ b/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py @@ -626,7 +626,7 @@ async def test_getattr_does_not_block_inside_running_loop_on_expired_token(monke assert refresh_calls["count"] == 1 -def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch): +def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch, unset_database_url): """When DATABASE_PORT is unset, the writer must default to the Postgres standard port instead of passing `None` through. Passing None to `generate_iam_auth_token` makes botocore embed the literal string @@ -639,7 +639,6 @@ def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch): monkeypatch.setenv("DATABASE_USER", "litellm") monkeypatch.setenv("DATABASE_NAME", "litellm") monkeypatch.delenv("DATABASE_SCHEMA", raising=False) - monkeypatch.delenv("DATABASE_URL", raising=False) captured: Dict[str, Any] = {} @@ -661,7 +660,7 @@ def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch): assert ":5432/litellm" in (new_url or "") -def test_writer_get_rds_iam_token_uses_database_host_env_vars(monkeypatch): +def test_writer_get_rds_iam_token_uses_database_host_env_vars(monkeypatch, unset_database_url): """Writer's IAM path (no iam_endpoint configured) reads host/port/user/db from the legacy DATABASE_HOST/PORT/USER/NAME env vars and writes the URL back to DATABASE_URL — this is the pre-read-replica behavior the patch @@ -673,7 +672,6 @@ def test_writer_get_rds_iam_token_uses_database_host_env_vars(monkeypatch): monkeypatch.setenv("DATABASE_USER", "litellm") monkeypatch.setenv("DATABASE_NAME", "litellm") monkeypatch.setenv("DATABASE_SCHEMA", "public") - monkeypatch.delenv("DATABASE_URL", raising=False) captured: Dict[str, Any] = {} From 65d0dcfb821adcc80a1e78dc48e37df71f6eda89 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 15:02:05 -0700 Subject: [PATCH 075/399] fix(mcp): never forward an Authorization header that satisfied admission on the tools preview Authorization doubles as the admission fallback when x-litellm-api-key is absent, so a caller who authenticated the preview request that way had their LiteLLM key forwarded to the upstream as the oauth2/client-forwarded token. The preview now forwards Authorization only when the primary admission header is present, which is how the dashboard has always sent it; with no primary header there is no upstream token on the request at all. Applies to oauth2 and both client-forwarded modes; parametrized regression test plus the admission header added to the existing extraction tests to mirror the real UI request shape --- .../mcp_server/rest_endpoints.py | 5 +- .../mcp_server/test_rest_endpoints.py | 141 +++++++++--------- 2 files changed, 71 insertions(+), 75 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index 21682b4dd3e..cae304bf73a 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -1321,12 +1321,15 @@ if MCP_AVAILABLE: if isinstance(credentials, dict): mcp_auth_header = credentials.get("auth_value") + # Authorization doubles as the admission fallback (LITELLM_API_KEY_HEADER_NAME_SECONDARY): + # when the primary x-litellm-api-key header is absent, the Authorization value is the + # caller's LiteLLM key, not an upstream token, and must never be forwarded upstream. oauth2_headers: Optional[Dict[str, str]] = None if new_mcp_server_request.auth_type in { MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate, - }: + } and headers.get(MCPRequestHandler.LITELLM_API_KEY_HEADER_NAME_PRIMARY): oauth2_headers = MCPRequestHandler._get_oauth2_headers_from_headers(headers) async def _list_tools_operation(client): diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py index 090b4711dc9..465cebcc12c 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py @@ -37,10 +37,7 @@ def _build_request( body_bytes = body else: body_bytes = b"" - raw_headers = [ - (key.lower().encode("latin-1"), value.encode("latin-1")) - for key, value in headers.items() - ] + raw_headers = [(key.lower().encode("latin-1"), value.encode("latin-1")) for key, value in headers.items()] scope = { "type": "http", "http_version": "1.1", @@ -62,25 +59,18 @@ def _build_request( def _get_route(path: str, method: str): for route in rest_endpoints.router.routes: - if getattr(route, "path", None) == path and method in getattr( - route, "methods", set() - ): + if getattr(route, "path", None) == path and method in getattr(route, "methods", set()): return route raise AssertionError(f"Route {method} {path} not found") def _route_has_dependency(route, dependency) -> bool: - if any( - getattr(dep, "dependency", None) == dependency - for dep in getattr(route, "dependencies", []) - ): + if any(getattr(dep, "dependency", None) == dependency for dep in getattr(route, "dependencies", [])): return True dependant = getattr(route, "dependant", None) if dependant is None: return False - return any( - getattr(dep, "call", None) == dependency for dep in dependant.dependencies - ) + return any(getattr(dep, "call", None) == dependency for dep in dependant.dependencies) class TestExecuteWithMcpClient: @@ -104,9 +94,7 @@ class TestExecuteWithMcpClient: auth_type=MCPAuth.none, ) - result = await rest_endpoints._execute_with_mcp_client( - payload, failing_operation - ) + result = await rest_endpoints._execute_with_mcp_client(payload, failing_operation) assert result["status"] == "error" assert "stack_trace" not in result @@ -267,15 +255,10 @@ class TestExecuteWithMcpClient: assert result["status"] == "ok" # The incoming Authorization must be dropped — extra_headers should # contain no oauth2 headers (only static_headers, which are None here). - assert ( - captured["extra_headers"] is None - or "Authorization" not in captured["extra_headers"] - ) + assert captured["extra_headers"] is None or "Authorization" not in captured["extra_headers"] @pytest.mark.asyncio - async def test_interactive_oauth_resolves_forwarded_token_via_presented_store( - self, monkeypatch - ): + async def test_interactive_oauth_resolves_forwarded_token_via_presented_store(self, monkeypatch): """Interactive authorization_code preview (oauth2, no client credentials): the forwarded just-authorized token is resolved THROUGH the v2 resolver via a one-shot presented store (cred_provider), not the caller-override path. The bare token (Bearer stripped) is the @@ -433,9 +416,7 @@ class TestExecuteWithMcpClient: return None async def fake_create_client(*args, **kwargs): - raise BaseExceptionGroup( - "test group", [RuntimeError("Cancelled via cancel scope")] - ) + raise BaseExceptionGroup("test group", [RuntimeError("Cancelled via cancel scope")]) monkeypatch.setattr( rest_endpoints.global_mcp_server_manager, @@ -497,9 +478,7 @@ class TestTestToolsList: "message": "Successfully retrieved tools", } - monkeypatch.setattr( - rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False - ) + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) oauth_call_counter = {"count": 0} @@ -555,9 +534,7 @@ class TestTestToolsList: "message": "Successfully retrieved tools", } - monkeypatch.setattr( - rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False - ) + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) oauth_headers = {"Authorization": "Bearer oauth"} oauth_call_counter = {"count": 0} @@ -573,7 +550,7 @@ class TestTestToolsList: raising=False, ) - request = _build_request({"authorization": "Bearer incoming"}) + request = _build_request({"authorization": "Bearer incoming", "x-litellm-api-key": "sk-admission"}) payload = NewMCPServerRequest( server_name="example", url="https://example.com", @@ -627,7 +604,7 @@ class TestTestToolsList: raising=False, ) - request = _build_request({"authorization": "Bearer upstream-token"}) + request = _build_request({"authorization": "Bearer upstream-token", "x-litellm-api-key": "sk-admission"}) payload = NewMCPServerRequest( server_name="example", url="https://example.com", @@ -646,6 +623,48 @@ class TestTestToolsList: assert captured["mcp_auth_header"] is None assert captured["oauth2_headers"] == oauth_headers + @pytest.mark.parametrize("auth_type", [MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_does_not_forward_authorization_that_satisfied_admission(self, monkeypatch, auth_type): + """Authorization is also the admission fallback: with no x-litellm-api-key on the request, + the Authorization value is the caller's LiteLLM key, so forwarding it would send the + admission credential to the upstream.""" + + captured: dict = {} + + async def fake_execute( + request, + operation, + mcp_auth_header=None, + oauth2_headers=None, + raw_headers=None, + ): + captured["oauth2_headers"] = oauth2_headers + return { + "tools": [], + "error": None, + "message": "Successfully retrieved tools", + } + + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) + + request = _build_request({"authorization": "Bearer sk-litellm-admission-key"}) + payload = NewMCPServerRequest( + server_name="example", + url="https://example.com", + auth_type=auth_type, + ) + + from litellm.proxy._types import LitellmUserRoles + + result = await rest_endpoints.test_tools_list( + request, + payload, + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert result["message"] == "Successfully retrieved tools" + assert captured["oauth2_headers"] is None + class TestListToolsRestAPI: pytestmark = pytest.mark.asyncio @@ -775,9 +794,7 @@ class TestListToolsRestAPI: stub_server = StubServer() captured = {} - async def fake_get_tools( - server, server_auth_header, *args, apply_tool_filters=True, **kwargs - ): + async def fake_get_tools(server, server_auth_header, *args, apply_tool_filters=True, **kwargs): captured["apply_tool_filters"] = apply_tool_filters return ["tool-1"] @@ -825,9 +842,7 @@ class TestListToolsRestAPI: assert captured["apply_tool_filters"] is True @pytest.mark.parametrize("upstream_status", [401, 403]) - async def test_upstream_auth_failure_surfaces_status_and_challenge( - self, monkeypatch, upstream_status - ): + async def test_upstream_auth_failure_surfaces_status_and_challenge(self, monkeypatch, upstream_status): """A single-server pass-through request whose upstream rejects the token must surface the upstream status (401 or 403) plus its WWW-Authenticate challenge, not collapse into a 200 ``unexpected_error`` body.""" @@ -1415,9 +1430,7 @@ class TestListToolsRestAPI: oauth_headers = {"Authorization": "Bearer user-oauth-token"} - async def fake_get_user_oauth_extra_headers( - server, user_api_key_dict, prefetched_creds=None - ): + async def fake_get_user_oauth_extra_headers(server, user_api_key_dict, prefetched_creds=None): return oauth_headers captured = {} @@ -1661,9 +1674,7 @@ class TestGetToolsForSingleServer: pytestmark = pytest.mark.asyncio - async def test_filters_tools_by_object_permission_mcp_tool_permissions( - self, monkeypatch - ): + async def test_filters_tools_by_object_permission_mcp_tool_permissions(self, monkeypatch): """Test that tools are filtered by user_api_key_auth.object_permission.mcp_tool_permissions""" from litellm.proxy._experimental.mcp_server.server import MCPServer from litellm.proxy._types import LiteLLM_ObjectPermissionTable @@ -1826,9 +1837,7 @@ class TestGetToolsForSingleServer: # All tools should be returned assert len(result) == 2 - async def test_no_filtering_when_server_not_in_mcp_tool_permissions( - self, monkeypatch - ): + async def test_no_filtering_when_server_not_in_mcp_tool_permissions(self, monkeypatch): """Test that all tools are returned when server is not in mcp_tool_permissions""" from litellm.proxy._experimental.mcp_server.server import MCPServer from litellm.proxy._types import LiteLLM_ObjectPermissionTable @@ -1881,9 +1890,7 @@ class TestGetToolsForSingleServer: # All tools should be returned since server is not in permissions assert len(result) == 2 - async def test_combines_server_allowed_tools_and_object_permission_filters( - self, monkeypatch - ): + async def test_combines_server_allowed_tools_and_object_permission_filters(self, monkeypatch): """Test that both server.allowed_tools and object_permission.mcp_tool_permissions filters are applied""" from litellm.proxy._experimental.mcp_server.server import MCPServer from litellm.proxy._types import LiteLLM_ObjectPermissionTable @@ -2201,9 +2208,7 @@ class TestPreviewOpenAPITools: "paths": { "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs": { "get": { - "operationId": ( - "actions/download-job-logs-for-workflow-run" - ), + "operationId": ("actions/download-job-logs-for-workflow-run"), "summary": "Download job logs", } }, @@ -2246,9 +2251,7 @@ class TestPreviewOpenAPITools: names = [t["name"] for t in result["tools"]] anthropic_re = re.compile(r"^[a-zA-Z0-9_-]{1,128}$") for name in names: - assert anthropic_re.match( - name - ), f"preview tool name {name!r} violates ^[a-zA-Z0-9_-]+$" + assert anthropic_re.match(name), f"preview tool name {name!r} violates ^[a-zA-Z0-9_-]+$" assert "actions_download-job-logs-for-workflow-run" in names assert "pulls_list-files" in names @@ -2305,9 +2308,7 @@ class TestPreviewOpenAPITools: registered_summary_to_name: dict = {} - def fake_create_tool_function( - path, method, operation, base_url - ): # noqa: ANN001 + def fake_create_tool_function(path, method, operation, base_url): # noqa: ANN001 def _f(): return None @@ -2320,9 +2321,7 @@ class TestPreviewOpenAPITools: ) class _StubRegistry: - def register_tool( - self, name, description, input_schema, handler - ): # noqa: ANN001 + def register_tool(self, name, description, input_schema, handler): # noqa: ANN001 registered_summary_to_name[description] = name monkeypatch.setattr( @@ -2331,9 +2330,7 @@ class TestPreviewOpenAPITools: _StubRegistry(), ) - openapi_to_mcp_generator.register_tools_from_openapi( - spec, base_url="https://example.invalid" - ) + openapi_to_mcp_generator.register_tools_from_openapi(spec, base_url="https://example.invalid") assert preview_summary_to_name == registered_summary_to_name, ( f"preview {preview_summary_to_name} != " @@ -2361,15 +2358,11 @@ class TestConnectionErrorMessage: assert secret not in message def test_connect_error_points_at_reachability(self): - message = rest_endpoints._connection_error_message( - httpx.ConnectError("All connection attempts failed") - ) + message = rest_endpoints._connection_error_message(httpx.ConnectError("All connection attempts failed")) assert "unreachable" in message.lower() def test_timeout_error_message(self): - message = rest_endpoints._connection_error_message( - httpx.ConnectTimeout("timed out") - ) + message = rest_endpoints._connection_error_message(httpx.ConnectTimeout("timed out")) assert "unreachable" in message.lower() def test_http_status_error_includes_status_code(self): From f4e1e8eb68cc34b971c7cc71e77db18d99804184 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 15:03:44 -0700 Subject: [PATCH 076/399] feat(ui): add shared composable DataTable component Phase 0 of the dashboard table-standardization effort: one composable DataTable built on TanStack react-table and the shadcn-style primitives in components/ui/table.tsx (Base UI, Tailwind v4), plus its behavioral test suite. No existing tables are migrated in this change. The component owns the TanStack instance and a shadcn shell, and exposes composable slots (toolbar, pagination, footer) plus DataTableToolbar, DataTablePagination, DataTableViewOptions, and DataTableSortHeader. Sorting and pagination each use a single mode enum (none/client/server) so server modes only surface state via callbacks and never reorder or slice locally. columnMeta.ts defines the canonical ColumnMeta augmentation. The rendering shell imports only components/ui/table primitives; no tremor or antd. --- .../shared/DataTable/DataTable.test.tsx | 431 +++++++++++++++ .../components/shared/DataTable/DataTable.tsx | 497 ++++++++++++++++++ .../DataTable/DataTablePagination.test.tsx | 68 +++ .../shared/DataTable/DataTablePagination.tsx | 113 ++++ .../DataTable/DataTableSortHeader.test.tsx | 112 ++++ .../shared/DataTable/DataTableSortHeader.tsx | 96 ++++ .../DataTable/DataTableToolbar.test.tsx | 35 ++ .../shared/DataTable/DataTableToolbar.tsx | 55 ++ .../shared/DataTable/DataTableViewOptions.tsx | 55 ++ .../components/shared/DataTable/columnMeta.ts | 13 + .../src/components/shared/DataTable/index.ts | 16 + .../src/components/shared/DataTable/types.ts | 60 +++ 12 files changed, 1551 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTablePagination.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/DataTableViewOptions.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/index.ts create mode 100644 ui/litellm-dashboard/src/components/shared/DataTable/types.ts diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx new file mode 100644 index 00000000000..df42c156975 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx @@ -0,0 +1,431 @@ +import type { ColumnDef, ExpandedState } from "@tanstack/react-table"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { useState } from "react"; +import { describe, expect, it, vi } from "vitest"; + +import { DataTable } from "./DataTable"; +import { DataTableSortHeader } from "./DataTableSortHeader"; +import { DataTableViewOptions } from "./DataTableViewOptions"; + +interface Person { + id: string; + name: string; + email: string; + flagged?: boolean; +} + +function person(id: string, name: string, flagged = false): Person { + return { id, name, email: `${name.toLowerCase()}@x.io`, flagged }; +} + +const names = (): (string | null)[] => screen.getAllByTestId("name-cell").map((el) => el.textContent); + +const nameCellColumns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => {row.original.name}, + }, +]; + +const headerCycleColumns: ColumnDef[] = [ + { + accessorKey: "name", + header: ({ column }) => , + cell: ({ row }) => {row.original.name}, + }, +]; + +const dropdownSortColumns: ColumnDef[] = [ + { + accessorKey: "name", + header: ({ column }) => , + cell: ({ row }) => {row.original.name}, + }, +]; + +const nameEmailColumns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => {row.original.name}, + }, + { + accessorKey: "email", + header: "Email", + cell: ({ row }) => {row.original.email}, + }, +]; + +const pinnedColumns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => {row.original.name}, + meta: { pinned: "left" }, + }, + { + accessorKey: "email", + header: "Email", + cell: ({ row }) => {row.original.email}, + }, +]; + +const rowClickColumns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => {row.original.name}, + }, + { + id: "actions", + header: "Actions", + cell: () => ( +

+ + +
+ ), + }, +]; + +const expansionColumns: ColumnDef[] = [ + { + id: "expander", + header: "", + cell: ({ row }) => ( + + ), + }, + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => {row.original.name}, + }, +]; + +const CHARLIE_ALICE_BOB: Person[] = [person("c", "Charlie"), person("a", "Alice"), person("b", "Bob")]; + +describe("DataTable sorting", () => { + it("client mode reorders rows when the sort header is clicked", async () => { + const user = userEvent.setup(); + render(); + + expect(names()).toEqual(["Charlie", "Alice", "Bob"]); + await user.click(screen.getByTestId("sort-header-name")); + expect(names()).toEqual(["Alice", "Bob", "Charlie"]); + }); + + it("server mode fires the callback but never reorders locally", async () => { + const user = userEvent.setup(); + const onSortingChange = vi.fn(); + render( + , + ); + + // sorting state says ascending, but server mode must render data as given + expect(names()).toEqual(["Charlie", "Alice", "Bob"]); + await user.click(screen.getByTestId("sort-header-name")); + expect(onSortingChange).toHaveBeenCalledTimes(1); + expect(names()).toEqual(["Charlie", "Alice", "Bob"]); + }); + + it("dropdown-tristate variant sorts ascending, descending, then resets", async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByTestId("sort-trigger-name")); + await user.click(await screen.findByText("Ascending")); + expect(names()).toEqual(["Alice", "Bob", "Charlie"]); + + await user.click(screen.getByTestId("sort-trigger-name")); + await user.click(await screen.findByText("Descending")); + expect(names()).toEqual(["Charlie", "Bob", "Alice"]); + + await user.click(screen.getByTestId("sort-trigger-name")); + await user.click(await screen.findByText("Reset")); + expect(names()).toEqual(["Charlie", "Alice", "Bob"]); + }); +}); + +describe("DataTable pagination", () => { + const fivePeople: Person[] = Array.from({ length: 5 }, (_, i) => person(String(i), `P${i}`)); + + it("client mode slices rows and advances pages", async () => { + const user = userEvent.setup(); + render(); + + expect(names()).toEqual(["P0", "P1"]); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-2 of 5"); + + await user.click(screen.getByTestId("pagination-next")); + expect(names()).toEqual(["P2", "P3"]); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 3-4 of 5"); + }); + + it("server mode shows X-Y of Z from rowCount and does NOT slice the given rows", async () => { + const user = userEvent.setup(); + const onPaginationChange = vi.fn(); + const pageSlice: Person[] = [person("10", "P10"), person("11", "P11"), person("12", "P12")]; + render( + , + ); + + expect(names()).toEqual(["P10", "P11", "P12"]); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 11-20 of 25"); + + await user.click(screen.getByTestId("pagination-next")); + expect(onPaginationChange).toHaveBeenCalledTimes(1); + }); +}); + +describe("DataTable column visibility", () => { + it("hides a column when toggled off in the view-options menu", async () => { + const user = userEvent.setup(); + render( + } + />, + ); + + expect(screen.getByText("Email")).toBeInTheDocument(); + await user.click(screen.getByTestId("view-options-trigger")); + await user.click(await screen.findByTestId("view-option-email")); + await waitFor(() => expect(screen.queryByText("Email")).not.toBeInTheDocument()); + + await user.click(screen.getByTestId("view-option-email")); + await waitFor(() => expect(screen.getByText("Email")).toBeInTheDocument()); + }); + + it("omits columns that opt out of hiding from the menu", async () => { + const user = userEvent.setup(); + const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Name", + enableHiding: false, + cell: ({ row }) => {row.original.name}, + }, + { + accessorKey: "email", + header: "Email", + cell: ({ row }) => {row.original.email}, + }, + ]; + render( + } + />, + ); + + await user.click(screen.getByTestId("view-options-trigger")); + expect(await screen.findByTestId("view-option-email")).toBeInTheDocument(); + expect(screen.queryByTestId("view-option-name")).toBeNull(); + }); +}); + +describe("DataTable pinned columns", () => { + it("applies sticky positioning to a pinned column only", () => { + const { container } = render(); + + const pinnedHead = container.querySelector('th[data-header-id="name"]'); + const normalHead = container.querySelector('th[data-header-id="email"]'); + + expect(pinnedHead?.style.position).toBe("sticky"); + expect(pinnedHead?.style.left).toBe("0px"); + expect(normalHead?.style.position).toBe(""); + }); +}); + +describe("DataTable row click guard", () => { + it("fires onRowClick from a plain cell but not from interactive elements", async () => { + const user = userEvent.setup(); + const onRowClick = vi.fn(); + render(); + + await user.click(screen.getByTestId("name-cell")); + expect(onRowClick).toHaveBeenCalledTimes(1); + expect(onRowClick).toHaveBeenCalledWith(expect.objectContaining({ id: "a" })); + + await user.click(screen.getByTestId("row-button")); + expect(onRowClick).toHaveBeenCalledTimes(1); + + await user.click(screen.getByTestId("row-input")); + expect(onRowClick).toHaveBeenCalledTimes(1); + }); +}); + +describe("DataTable expansion", () => { + const subComponent = ({ row }: { row: { original: Person } }) => ( +
details for {row.original.name}
+ ); + + it("toggles the sub-row in uncontrolled mode", async () => { + const user = userEvent.setup(); + render( + row.id} + getRowCanExpand={() => true} + renderSubComponent={subComponent} + />, + ); + + expect(screen.queryByTestId("sub-row")).not.toBeInTheDocument(); + await user.click(screen.getByTestId("expand-a")); + expect(screen.getByTestId("sub-row")).toBeInTheDocument(); + await user.click(screen.getByTestId("expand-a")); + expect(screen.queryByTestId("sub-row")).not.toBeInTheDocument(); + }); + + it("toggles the sub-row in controlled mode driven by parent state", async () => { + const user = userEvent.setup(); + const Harness = () => { + const [expanded, setExpanded] = useState({}); + return ( + row.id} + expanded={expanded} + onExpandedChange={setExpanded} + getRowCanExpand={() => true} + renderSubComponent={subComponent} + /> + ); + }; + render(); + + expect(screen.queryByTestId("sub-row")).not.toBeInTheDocument(); + await user.click(screen.getByTestId("expand-a")); + expect(screen.getByTestId("sub-row")).toBeInTheDocument(); + await user.click(screen.getByTestId("expand-a")); + expect(screen.queryByTestId("sub-row")).not.toBeInTheDocument(); + }); + + it("stays collapsed in controlled mode when the parent ignores the change", async () => { + const user = userEvent.setup(); + const onExpandedChange = vi.fn(); + render( + row.id} + expanded={{}} + onExpandedChange={onExpandedChange} + getRowCanExpand={() => true} + renderSubComponent={subComponent} + />, + ); + + await user.click(screen.getByTestId("expand-a")); + expect(onExpandedChange).toHaveBeenCalledTimes(1); + expect(screen.queryByTestId("sub-row")).not.toBeInTheDocument(); + }); +}); + +describe("DataTable row styling and footer", () => { + it("applies rowClassName to the matching row only", () => { + const data = [person("a", "Alice", true), person("b", "Bob", false)]; + const { container } = render( + row.id} + rowClassName={(row) => (row.original.flagged ? "flagged-row" : "")} + />, + ); + + expect(container.querySelector('tr[data-row-id="a"]')?.className).toContain("flagged-row"); + expect(container.querySelector('tr[data-row-id="b"]')?.className).not.toContain("flagged-row"); + }); + + it("renders the footer slot inside a tfoot element", () => { + render( + ( +
Total: 3
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + +
{flexRender(header.column.columnDef.header, header.getContext())}
+ ); +} + +describe("DataTableSortHeader", () => { + it("renders a plain label and no button when the column cannot sort", () => { + render(); + expect(screen.queryByTestId("sort-header-name")).toBeNull(); + expect(screen.getByText("Name")).toBeInTheDocument(); + }); + + it("header-cycle indicator advances none -> asc -> desc on click", async () => { + const user = userEvent.setup(); + render(); + const indicator = () => screen.getByTestId("sort-header-name").querySelector("[data-sort-indicator]"); + + expect(indicator()).toHaveAttribute("data-sort-indicator", "none"); + await user.click(screen.getByTestId("sort-header-name")); + expect(indicator()).toHaveAttribute("data-sort-indicator", "asc"); + await user.click(screen.getByTestId("sort-header-name")); + expect(indicator()).toHaveAttribute("data-sort-indicator", "desc"); + }); + + it("dropdown-tristate sets ascending, descending, and reset from the menu", async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByTestId("sort-trigger-name")); + await user.click(await screen.findByText("Descending")); + expect(screen.getByTestId("sort-trigger-name").querySelector('[data-sort-indicator="desc"]')).not.toBeNull(); + + await user.click(screen.getByTestId("sort-trigger-name")); + await user.click(await screen.findByText("Ascending")); + expect(screen.getByTestId("sort-trigger-name").querySelector('[data-sort-indicator="asc"]')).not.toBeNull(); + + await user.click(screen.getByTestId("sort-trigger-name")); + await user.click(await screen.findByText("Reset")); + expect(screen.getByTestId("sort-trigger-name").querySelector('[data-sort-indicator="none"]')).not.toBeNull(); + }); + + it("dropdown-tristate trigger stops the click from reaching an outer handler", async () => { + const user = userEvent.setup(); + const onOuterClick = vi.fn(); + render( +
+ +
, + ); + + await user.click(screen.getByTestId("sort-trigger-name")); + expect(onOuterClick).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.tsx new file mode 100644 index 00000000000..1cf09ce4f47 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Menu } from "@base-ui/react/menu"; +import type { Column, SortDirection } from "@tanstack/react-table"; +import { ChevronDown, ChevronsUpDown, ChevronUp, X } from "lucide-react"; +import type * as React from "react"; + +import { cn } from "@/lib/cva.config"; + +export type DataTableSortVariant = "header-cycle" | "dropdown-tristate"; + +interface DataTableSortHeaderProps { + column: Column; + title: React.ReactNode; + variant?: DataTableSortVariant; + className?: string; +} + +function SortIndicator({ sorted }: { sorted: false | SortDirection }) { + if (sorted === "asc") { + return ; + } + if (sorted === "desc") { + return ; + } + return ; +} + +const MENU_ITEM_CLASS = + "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground"; + +export function DataTableSortHeader({ + column, + title, + variant = "header-cycle", + className, +}: DataTableSortHeaderProps) { + const sorted = column.getIsSorted(); + + if (!column.getCanSort()) { + return {title}; + } + + if (variant === "dropdown-tristate") { + return ( +
+ {title} + + event.stopPropagation()} + className={cn( + "inline-flex size-6 items-center justify-center rounded-md hover:bg-muted", + sorted ? "text-primary" : "text-muted-foreground", + )} + > + + + } + /> + + + + column.toggleSorting(false)}> + Ascending + + column.toggleSorting(true)}> + Descending + + column.clearSorting()}> + Reset + + + + + +
+ ); + } + + return ( + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx new file mode 100644 index 00000000000..5d1f5340f5d --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx @@ -0,0 +1,35 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; + +import { DataTableToolbar } from "./DataTableToolbar"; + +describe("DataTableToolbar", () => { + it("renders slotted action children", () => { + render( + + + , + ); + expect(screen.getByTestId("toolbar-action")).toBeInTheDocument(); + }); + + it("shows the reset button only when there are active filters", async () => { + const user = userEvent.setup(); + const onResetFilters = vi.fn(); + const { rerender } = render(); + expect(screen.queryByText("Reset Filters")).toBeNull(); + + rerender(); + await user.click(screen.getByText("Reset Filters")); + expect(onResetFilters).toHaveBeenCalledTimes(1); + }); + + it("wires the filters toggle button", async () => { + const user = userEvent.setup(); + const onToggleFilters = vi.fn(); + render(); + await user.click(screen.getByText("Filters")); + expect(onToggleFilters).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.tsx new file mode 100644 index 00000000000..80b4ecc7edd --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.tsx @@ -0,0 +1,55 @@ +"use client"; + +import { Search } from "lucide-react"; +import type * as React from "react"; + +import { FilterInput } from "@/components/common_components/Filters/FilterInput"; +import { FiltersButton } from "@/components/common_components/Filters/FiltersButton"; +import { ResetFiltersButton } from "@/components/common_components/Filters/ResetFiltersButton"; +import { cn } from "@/lib/cva.config"; + +interface DataTableToolbarProps { + searchValue?: string; + onSearchChange?: (value: string) => void; + searchPlaceholder?: string; + filtersActive?: boolean; + hasActiveFilters?: boolean; + onToggleFilters?: () => void; + onResetFilters?: () => void; + children?: React.ReactNode; + className?: string; +} + +export function DataTableToolbar({ + searchValue, + onSearchChange, + searchPlaceholder = "Search", + filtersActive = false, + hasActiveFilters = false, + onToggleFilters, + onResetFilters, + children, + className, +}: DataTableToolbarProps) { + const showReset = onResetFilters !== undefined && hasActiveFilters; + + return ( +
+
+ {onSearchChange !== undefined && ( + + )} + {onToggleFilters !== undefined && ( + + )} + {showReset && } +
+ {children !== undefined &&
{children}
} +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableViewOptions.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableViewOptions.tsx new file mode 100644 index 00000000000..ab56aafe7b5 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableViewOptions.tsx @@ -0,0 +1,55 @@ +"use client"; + +import { Menu } from "@base-ui/react/menu"; +import type { Table } from "@tanstack/react-table"; +import { Check, SlidersHorizontal } from "lucide-react"; + +import { Button } from "@/components/ui/button"; + +interface DataTableViewOptionsProps { + table: Table; + label?: string; + className?: string; +} + +export function DataTableViewOptions({ table, label = "View", className }: DataTableViewOptionsProps) { + const hideableColumns = table.getAllLeafColumns().filter((column) => column.getCanHide()); + + if (hideableColumns.length === 0) { + return null; + } + + return ( + + + + {label} + + } + /> + + + + {hideableColumns.map((column) => ( + column.toggleVisibility(checked)} + closeOnClick={false} + data-testid={`view-option-${column.id}`} + className="relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-7 capitalize outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground" + > + + + + {column.columnDef.meta?.title ?? column.id} + + ))} + + + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts b/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts new file mode 100644 index 00000000000..46e72226038 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts @@ -0,0 +1,13 @@ +import type { RowData } from "@tanstack/react-table"; + +import type { ColumnPinnedSide } from "./types"; + +declare module "@tanstack/react-table" { + interface ColumnMeta { + numeric?: boolean; + className?: string; + headerClassName?: string; + title?: string; + pinned?: ColumnPinnedSide; + } +} diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/index.ts b/ui/litellm-dashboard/src/components/shared/DataTable/index.ts new file mode 100644 index 00000000000..49a4430bbee --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/index.ts @@ -0,0 +1,16 @@ +import "./columnMeta"; + +export { DataTable, DataTableConfigError, validateDataTableConfig } from "./DataTable"; +export { DataTablePagination, DEFAULT_PAGE_SIZE_OPTIONS } from "./DataTablePagination"; +export { DataTableToolbar } from "./DataTableToolbar"; +export { DataTableViewOptions } from "./DataTableViewOptions"; +export { DataTableSortHeader, type DataTableSortVariant } from "./DataTableSortHeader"; +export type { DataTablePaginationProps } from "./DataTablePagination"; +export type { + ColumnPinnedSide, + ColumnResizeMode, + DataTableProps, + DataTableSize, + PaginationMode, + SortingMode, +} from "./types"; diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/types.ts b/ui/litellm-dashboard/src/components/shared/DataTable/types.ts new file mode 100644 index 00000000000..8fa6f21c4d3 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DataTable/types.ts @@ -0,0 +1,60 @@ +import type { + ColumnDef, + ExpandedState, + OnChangeFn, + PaginationState, + Row, + RowData, + SortingState, + Table, + VisibilityState, +} from "@tanstack/react-table"; +import type * as React from "react"; + +export type SortingMode = "none" | "client" | "server"; +export type PaginationMode = "none" | "client" | "server"; +export type ColumnResizeMode = "onEnd" | "onChange"; +export type DataTableSize = "compact" | "default"; +export type ColumnPinnedSide = "left" | "right"; + +export interface DataTableProps { + data: TData[]; + columns: ColumnDef[]; + getRowId?: (row: TData, index: number, parent?: Row) => string; + + isLoading?: boolean; + loadingMessage?: string; + noDataMessage?: React.ReactNode; + + sortingMode?: SortingMode; + sorting?: SortingState; + onSortingChange?: OnChangeFn; + defaultSorting?: SortingState; + enableSortingRemoval?: boolean; + + paginationMode?: PaginationMode; + pagination?: PaginationState; + onPaginationChange?: OnChangeFn; + rowCount?: number; + pageSizeOptions?: number[]; + + enableColumnResizing?: boolean; + columnResizeMode?: ColumnResizeMode; + defaultColumnVisibility?: VisibilityState; + + getRowCanExpand?: (row: Row) => boolean; + renderSubComponent?: (props: { row: Row }) => React.ReactElement; + expanded?: ExpandedState; + onExpandedChange?: OnChangeFn; + + onRowClick?: (row: TData) => void; + + rowClassName?: (row: Row) => string; + + maxBodyHeight?: number | string; + size?: DataTableSize; + + toolbar?: (table: Table) => React.ReactNode; + paginationSlot?: (table: Table) => React.ReactNode; + footer?: (table: Table) => React.ReactNode; +} From b0ff698addc9246a28f0f247669d487f43bb608f Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 15:21:52 -0700 Subject: [PATCH 077/399] refactor(ui): migrate Workflow Runs table onto shared DataTable Proof-of-concept consumer for the shared DataTable added in the previous commit. Swaps the antd Table in the Workflow Runs page for DataTable in client-pagination mode, keeping the existing cell renderers, row-click drawer, and empty state. Adds a focused test that the rows render through DataTable, a row click routes the detail fetch to the correct run, and the empty state shows. --- .../workflows/WorkflowRuns.test.tsx | 81 +++++++++++ .../(dashboard)/workflows/WorkflowRuns.tsx | 136 +++++++++--------- 2 files changed, 149 insertions(+), 68 deletions(-) create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx new file mode 100644 index 00000000000..e73abfe7cd6 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx @@ -0,0 +1,81 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import WorkflowRuns from "./WorkflowRuns"; + +vi.mock("@/components/networking", () => ({ proxyBaseUrl: "" })); + +interface FakeRun { + run_id: string; + status: string; + workflow_type: string; + created_at: string; + metadata: { title?: string; state?: string } | null; +} + +const RUNS: FakeRun[] = [ + { + run_id: "run-aaaaaaaa-1111", + status: "completed", + workflow_type: "grill", + created_at: "2026-01-01T00:00:00Z", + metadata: { title: "First run", state: "done" }, + }, + { + run_id: "run-bbbbbbbb-2222", + status: "running", + workflow_type: "autofix", + created_at: "2026-01-02T00:00:00Z", + metadata: null, + }, +]; + +function mockFetch(runs: FakeRun[]) { + return vi.fn((url: string) => { + if (url.includes("/runs?limit")) { + return Promise.resolve({ ok: true, json: () => Promise.resolve({ runs }) }); + } + if (url.includes("/events")) { + return Promise.resolve({ ok: true, json: () => Promise.resolve({ events: [] }) }); + } + if (url.includes("/messages")) { + return Promise.resolve({ ok: true, json: () => Promise.resolve({ messages: [] }) }); + } + return Promise.resolve({ ok: false, status: 404, json: () => Promise.resolve({}) }); + }); +} + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +describe("WorkflowRuns (migrated onto shared DataTable)", () => { + it("renders one DataTable row per fetched run", async () => { + vi.stubGlobal("fetch", mockFetch(RUNS)); + const { container } = render(); + + expect(await screen.findByText("First run")).toBeInTheDocument(); + expect(container.querySelectorAll("tr[data-row-id]")).toHaveLength(2); + }); + + it("opens the detail drawer for the clicked run by firing its detail fetch", async () => { + const user = userEvent.setup(); + const fetchSpy = mockFetch(RUNS); + vi.stubGlobal("fetch", fetchSpy); + render(); + + await user.click(await screen.findByText("First run")); + + await waitFor(() => + expect(fetchSpy).toHaveBeenCalledWith(expect.stringContaining("run-aaaaaaaa-1111/events"), expect.anything()), + ); + }); + + it("shows the empty state when there are no runs", async () => { + vi.stubGlobal("fetch", mockFetch([])); + render(); + + expect(await screen.findByText("No workflow runs yet")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx index 2aecbece2e3..b668cdee0bd 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx @@ -1,7 +1,9 @@ -import React, { useState, useEffect, useCallback } from "react"; -import { Button, Collapse, Drawer, Empty, Spin, Table, Tooltip, Typography } from "antd"; +import React, { useState, useEffect, useCallback, useMemo } from "react"; +import { Button, Collapse, Drawer, Empty, Spin, Tooltip, Typography } from "antd"; import { ReloadOutlined } from "@ant-design/icons"; +import type { ColumnDef } from "@tanstack/react-table"; import { proxyBaseUrl } from "@/components/networking"; +import { DataTable } from "@/components/shared/DataTable"; const { Text } = Typography; @@ -541,48 +543,54 @@ const WorkflowRuns: React.FC = ({ accessToken }) => { fetchRuns(); }, [fetchRuns]); - const columns = [ - { - title: "Run", - dataIndex: "run_id", - key: "run", - render: (_: string, run: WorkflowRun) => ( -
- -
-
{runTitle(run)}
-
{shortId(run.run_id)}
-
-
- ), - }, - { - title: "Type", - dataIndex: "workflow_type", - key: "workflow_type", - render: (v: string) => {v}, - }, - { - title: "Status", - dataIndex: "status", - key: "status", - render: (status: RunStatus, run: WorkflowRun) => { - const state = run.metadata?.state; - return ( -
- - {state ?? status} -
- ); + const columns = useMemo[]>( + () => [ + { + id: "run", + header: "Run", + cell: ({ row }) => { + const run = row.original; + return ( +
+ +
+
{runTitle(run)}
+
{shortId(run.run_id)}
+
+
+ ); + }, }, - }, - { - title: "Created", - dataIndex: "created_at", - key: "created_at", - render: (v: string) => {timeAgo(v)}, - }, - ]; + { + accessorKey: "workflow_type", + header: "Type", + cell: ({ row }) => ( + {row.original.workflow_type} + ), + }, + { + id: "status", + header: "Status", + cell: ({ row }) => { + const run = row.original; + return ( +
+ + + {run.metadata?.state ?? run.status} + +
+ ); + }, + }, + { + accessorKey: "created_at", + header: "Created", + cell: ({ row }) => {timeAgo(row.original.created_at)}, + }, + ], + [], + ); return (
= ({ accessToken }) => {
- {/* runs table — matches logs page density */} -
- ({ - onClick: () => fetchRunDetail(run), - style: { cursor: "pointer" }, - })} - locale={{ - emptyText: ( - No workflow runs yet} - image={Empty.PRESENTED_IMAGE_SIMPLE} - /> - ), - }} - className="[&_.ant-table-cell]:py-0.5 [&_.ant-table-thead_.ant-table-cell]:py-1" - style={{ border: "none" }} - /> - + run.run_id} + isLoading={loadingRuns} + loadingMessage="Loading workflow runs…" + noDataMessage={ + No workflow runs yet} + image={Empty.PRESENTED_IMAGE_SIMPLE} + /> + } + paginationMode="client" + pageSizeOptions={[50, 100]} + onRowClick={fetchRunDetail} + size="compact" + /> {/* detail drawer */} Date: Thu, 9 Jul 2026 15:35:28 -0700 Subject: [PATCH 078/399] fix(mcp): log only the origin of the upstream MCP url in tool-call metadata The redacted resource kept the path, but hosted MCP servers routinely embed the credential in the path (for example /mcp/s//mcp), and mcp_tool_call_metadata is readable by a caller who can invoke the tool, so the path leaked the upstream credential into spend logs. Only scheme, host, and port are logged now --- litellm/proxy/_experimental/mcp_server/server.py | 11 ++++++----- .../_experimental/mcp_server/test_mcp_server.py | 12 +++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 938cc2bc43b..3550237dd65 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -107,11 +107,12 @@ _MCP_ROUTING_PEEK_MAX_BYTES = 4096 def _redact_mcp_resource_url(url: Optional[str]) -> Optional[str]: - """Reduce an MCP server URL to its bare resource identifier for logging. + """Reduce an MCP server URL to its origin (scheme + host + port) for logging. - Keeps scheme, host, and path; drops userinfo (``user:pass@``), the query string, - and the fragment, so an upstream URL carrying an embedded token, userinfo, or a - secret query parameter never reaches spend-log metadata or logging callbacks. + Everything else is dropped: userinfo (``user:pass@``), the query string, the + fragment, and the path, because hosted MCP servers routinely embed the + credential in the path (e.g. ``/mcp/s/``) and this value is persisted + in spend-log metadata that a caller who can invoke the tool can read back. Returns None when the URL has no host to identify (nothing safe to log). """ if not isinstance(url, str) or not url: @@ -123,7 +124,7 @@ def _redact_mcp_resource_url(url: Optional[str]) -> Optional[str]: if not parts.hostname: return None netloc = f"{parts.hostname}:{parts.port}" if parts.port else parts.hostname - return urlunsplit((parts.scheme, netloc, parts.path, "", "")) or None + return urlunsplit((parts.scheme, netloc, "", "", "")) or None def _invalidate_byok_cred_cache(user_id: str, server_id: str) -> None: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index 0db36e75e48..bba0eb31cfb 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -6859,11 +6859,13 @@ async def test_call_tool_with_legacy_db_m2m_server_resolves_oauth2_flow(): @pytest.mark.parametrize( "url, expected", [ - # userinfo + secret query param must both be stripped from the logged resource - ("https://user:s3cr3t@mcp.example.com/mcp?token=abcd1234&x=1", "https://mcp.example.com/mcp"), - ("https://mcp.example.com/mcp#frag", "https://mcp.example.com/mcp"), - ("https://host:8443/a/b?q=1", "https://host:8443/a/b"), - ("https://mcp.notion.com/mcp", "https://mcp.notion.com/mcp"), + # only the origin may be logged: userinfo, query, fragment, and the PATH are all stripped, + # because hosted MCP servers routinely embed the credential in the path (e.g. /mcp/s/) + ("https://user:s3cr3t@mcp.example.com/mcp?token=abcd1234&x=1", "https://mcp.example.com"), + ("https://mcp.example.com/mcp#frag", "https://mcp.example.com"), + ("https://host:8443/a/b?q=1", "https://host:8443"), + ("https://mcp.zapier.com/api/mcp/s/NDgzcret-token/mcp", "https://mcp.zapier.com"), + ("https://mcp.notion.com/mcp", "https://mcp.notion.com"), (None, None), ("", None), ("not a url", None), From d4e02ac047565ed3c14e710a991436f369a08509 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 15:35:28 -0700 Subject: [PATCH 079/399] refactor(mcp): share _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES in the relay gate and tools preview The gateway authorize/token/register gate and the preview header extraction each carried their own inline copy of the oauth2 + client-forwarded mode set, which could drift from the discovery constant the registry builders use; all three surfaces mean the same thing (modes that run the upstream OAuth browser flow), so they now read the one constant --- .../_experimental/mcp_server/discoverable_endpoints.py | 6 +++++- litellm/proxy/_experimental/mcp_server/rest_endpoints.py | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 7606deac241..4fd47a97066 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -473,7 +473,11 @@ def _raise_if_not_oauth2(mcp_server: MCPServer) -> None: token is upstream-audienced and held by the caller; the gateway persists nothing for these modes (DCR persistence is opt-in and never enabled on this path). """ - if mcp_server.auth_type in (MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 # circular import with mcp_server_manager at module load + _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, + ) + + if mcp_server.auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES: return raise HTTPException( status_code=400, diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index cae304bf73a..74f0b488d20 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -69,6 +69,7 @@ if MCP_AVAILABLE: from mcp.types import Tool as MCPTool from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, global_mcp_server_manager, ) from litellm.proxy._experimental.mcp_server.oauth_utils import ( @@ -1325,11 +1326,9 @@ if MCP_AVAILABLE: # when the primary x-litellm-api-key header is absent, the Authorization value is the # caller's LiteLLM key, not an upstream token, and must never be forwarded upstream. oauth2_headers: Optional[Dict[str, str]] = None - if new_mcp_server_request.auth_type in { - MCPAuth.oauth2, - MCPAuth.true_passthrough, - MCPAuth.oauth_delegate, - } and headers.get(MCPRequestHandler.LITELLM_API_KEY_HEADER_NAME_PRIMARY): + if new_mcp_server_request.auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES and headers.get( + MCPRequestHandler.LITELLM_API_KEY_HEADER_NAME_PRIMARY + ): oauth2_headers = MCPRequestHandler._get_oauth2_headers_from_headers(headers) async def _list_tools_operation(client): From 1aa4cb0d2ac16eaa2b5712c916690094596930e9 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 16:08:18 -0700 Subject: [PATCH 080/399] refactor(ui): migrate Team Info virtual keys table onto shared DataTable Second proof-of-concept consumer for the shared DataTable. Replaces the hand-rolled tremor table in the Team Info Virtual Keys tab with DataTable in server-sort and server-pagination mode plus column resizing; the file drops about 150 lines. Sortable headers now use DataTableSortHeader, pagination is a detached DataTablePagination driven by the page state, the id-cell still opens the key drawer, and the body scrolls under a sticky header via maxBodyHeight. Two behavior changes: the pagination control is the standardized bar (row range plus page-size select) rather than the old Previous/Next buttons, and a sort header cycles ascending/descending without a third unsorted state, which also removes a latent case where clearing the sort left the server sorted. Updates the TeamVirtualKeysTable and TeamInfo tests to the new pagination, adds a test that a sort-header click routes to useKeys as a server sort, and lowers the no-large-inline-object-arg metric by one and the file's no-nested-ternary suppression from two to one to match the leaner code. --- ui/litellm-dashboard/eslint-metrics.json | 2 +- ui/litellm-dashboard/eslint-suppressions.json | 2 +- .../src/components/team/TeamInfo.test.tsx | 8 +- .../team/TeamVirtualKeysTable.test.tsx | 32 ++- .../components/team/TeamVirtualKeysTable.tsx | 235 +++--------------- 5 files changed, 74 insertions(+), 205 deletions(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index fcf60934f64..ad60a0d0c89 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,7 +1,7 @@ { "@typescript-eslint/no-explicit-any": 1980, "complexity": 128, - "local/no-large-inline-object-arg": 519, + "local/no-large-inline-object-arg": 518, "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index fe8f182c106..6d310a97dbf 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -2323,7 +2323,7 @@ }, "src/components/team/TeamVirtualKeysTable.tsx": { "no-nested-ternary": { - "count": 2 + "count": 1 }, "no-restricted-imports": { "count": 1 diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx index 46eac1c5772..4ccfb891417 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx @@ -536,7 +536,7 @@ describe("TeamInfoView", () => { await user.click(virtualKeysTab); await waitFor(() => { - expect(screen.getByText("Page 1 of 1")).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-5 of 5"); }); }); @@ -584,9 +584,9 @@ describe("TeamInfoView", () => { expect(screen.getByRole("button", { name: "Filters" })).toBeInTheDocument(); }); expect(screen.getByRole("button", { name: "Reset Filters" })).toBeInTheDocument(); - expect(screen.getByText("Page 1 of 1")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Previous" })).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Next" })).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-1 of 1"); + expect(screen.getByTestId("pagination-prev")).toBeInTheDocument(); + expect(screen.getByTestId("pagination-next")).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx index 954f5ea98c5..9eb0282580b 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx @@ -163,7 +163,7 @@ describe("TeamVirtualKeysTable", () => { expect(screen.getByText("bob_key_team1")).toBeInTheDocument(); }); - it("should show Page X of Y when multiple pages exist", async () => { + it("should show the current range from total_count when multiple pages exist", async () => { mockUseKeys.mockReturnValue({ data: { keys: [createMockKey()], @@ -179,7 +179,7 @@ describe("TeamVirtualKeysTable", () => { renderWithProviders(); await waitFor(() => { - expect(screen.getByText("Page 1 of 3")).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-50 of 100"); }); }); @@ -203,17 +203,39 @@ describe("TeamVirtualKeysTable", () => { renderWithProviders(); await waitFor(() => { - expect(screen.getByText("Page 1 of 3")).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-50 of 100"); }); - const nextButton = screen.getByRole("button", { name: "Next" }); - await user.click(nextButton); + await user.click(screen.getByTestId("pagination-next")); await waitFor(() => { expect(mockUseKeys).toHaveBeenLastCalledWith(2, 50, expect.objectContaining({ teamID: "team-1" })); }); }); + it("routes a sort-header click to useKeys as a server-side sort", async () => { + const user = userEvent.setup(); + mockUseKeys.mockReturnValue({ + data: { keys: [createMockKey()], total_count: 1, current_page: 1, total_pages: 1 } as KeysResponse, + isPending: false, + isFetching: false, + refetch: vi.fn(), + } as unknown as ReturnType); + + renderWithProviders(); + + await waitFor(() => expect(screen.getByTestId("sort-header-created_at")).toBeInTheDocument()); + await user.click(screen.getByTestId("sort-header-created_at")); + + await waitFor(() => + expect(mockUseKeys).toHaveBeenLastCalledWith( + 1, + 50, + expect.objectContaining({ sortBy: "created_at", sortOrder: "asc" }), + ), + ); + }); + it("should show Loading keys when isPending", async () => { mockUseKeys.mockReturnValue({ data: undefined, diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index b7128e642a5..909608d630f 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -1,20 +1,12 @@ -// TO-DO: Standardize tables eventually - "use client"; import { useKeys } from "@/app/(dashboard)/hooks/keys/useKeys"; import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; -import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline"; -import { - ColumnDef, - flexRender, - getCoreRowModel, - PaginationState, - SortingState, - useReactTable, -} from "@tanstack/react-table"; -import { Badge, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react"; +import { DataTable, DataTablePagination, DataTableSortHeader } from "@/components/shared/DataTable"; +import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; +import { ColumnDef, PaginationState, SortingState } from "@tanstack/react-table"; +import { Badge, Icon, Text } from "@tremor/react"; import { InfoCircleOutlined } from "@ant-design/icons"; -import { Popover, Skeleton, Tooltip, Typography } from "antd"; +import { Popover, Tooltip, Typography } from "antd"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; @@ -83,7 +75,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi })); }, [keys?.keys, organization?.organization_id]); - const pageCount = keys?.total_pages ?? 0; + const rowCount = keys?.total_count ?? 0; const [expandedAccordions, setExpandedAccordions] = useState>({}); const currentTeam: Team = useMemo( @@ -200,7 +192,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "token", accessorKey: "token", - header: "Key ID", + header: ({ column }) => , size: 100, enableSorting: true, cell: (info) => ( @@ -210,7 +202,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "key_alias", accessorKey: "key_alias", - header: "Key Alias", + header: ({ column }) => , size: 150, enableSorting: true, cell: (info) => { @@ -282,7 +274,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "created_at", accessorKey: "created_at", - header: "Created At", + header: ({ column }) => , size: 120, enableSorting: true, cell: (info) => , @@ -349,7 +341,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "updated_at", accessorKey: "updated_at", - header: "Updated At", + header: ({ column }) => , size: 120, enableSorting: true, cell: (info) => , @@ -383,7 +375,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "spend", accessorKey: "spend", - header: "Spend (USD)", + header: ({ column }) => , size: 100, enableSorting: true, cell: (info) => , @@ -391,7 +383,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "max_budget", accessorKey: "max_budget", - header: "Budget (USD)", + header: ({ column }) => , size: 110, enableSorting: true, cell: (info) => ( @@ -529,22 +521,6 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi [sorting, handleFilterChange], ); - const table = useReactTable({ - data: displayKeys, - columns, - columnResizeMode: "onChange", - columnResizeDirection: "ltr", - state: { sorting, pagination: tablePagination }, - onSortingChange: handleSortingChange, - onPaginationChange: setTablePagination, - getCoreRowModel: getCoreRowModel(), - // getSortedRowModel not needed — manualSorting: true delegates sorting to the server - enableSorting: true, - manualSorting: true, // Server sorts via useKeys. Avoid redundant client-side sort - manualPagination: true, - pageCount: pageCount, - }); - return (
{selectedKey ? ( @@ -566,165 +542,36 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi />
-
-
- {isLoading || isFetching ? ( - - ) : ( - - Page {pageIndex + 1} of {table.getPageCount()} - - )} - - {isLoading || isFetching ? ( - - ) : ( - - )} - - {isLoading || isFetching ? ( - - ) : ( - - )} -
-
-
-
-
-
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - { - const resizer = document.querySelector(`[data-header-id="${header.id}"] .resizer`); - if (resizer) (resizer as HTMLElement).style.opacity = "0.5"; - }} - onMouseLeave={() => { - const resizer = document.querySelector(`[data-header-id="${header.id}"] .resizer`); - if (resizer && !header.column.getIsResizing()) - (resizer as HTMLElement).style.opacity = "0"; - }} - onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined} - > -
-
- {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} -
- {header.id !== "actions" && header.column.getCanSort() && ( -
- {header.column.getIsSorted() ? ( - { - asc: , - desc: , - }[header.column.getIsSorted() as string] - ) : ( - - )} -
- )} -
header.column.resetSize()} - onMouseDown={header.getResizeHandler()} - onTouchStart={header.getResizeHandler()} - className={`resizer ${table.options.columnResizeDirection} ${ - header.column.getIsResizing() ? "isResizing" : "" - }`} - style={{ - position: "absolute", - right: 0, - top: 0, - height: "100%", - width: "5px", - background: header.column.getIsResizing() ? "#3b82f6" : "transparent", - cursor: "col-resize", - userSelect: "none", - touchAction: "none", - opacity: header.column.getIsResizing() ? 1 : 0, - }} - /> -
- - ))} - - ))} - - - {isLoading || isFetching ? ( - - -
-

Loading keys...

-
-
-
- ) : displayKeys.length > 0 ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - 3 - ? "px-0" - : "" - }`} - > - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - -
-

No keys found

-
-
-
- )} -
-
-
-
+
+ setTablePagination((prev) => ({ ...prev, pageIndex: nextPage }))} + onPageSizeChange={(nextSize) => setTablePagination({ pageIndex: 0, pageSize: nextSize })} + isLoading={isLoading || isFetching} + />
+ + null} + enableColumnResizing + columnResizeMode="onChange" + isLoading={isLoading || isFetching} + loadingMessage="Loading keys..." + noDataMessage="No keys found" + maxBodyHeight="75vh" + size="compact" + />
)}
From 05f39bf9427290e077a6ef07c6d964cc90ef44df Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 10:43:13 -0700 Subject: [PATCH 081/399] fix(mcp): invalidate a browser-authorized upstream token when a mint-relevant field changes An admin who ran Authorize & Fetch and then changed a field that determines which upstream OAuth token gets minted kept using the stale token for tool preview, sessionStorage, and (on the backend) the stored per-user credential and its cache. Grounded in RFC 8707/8693 and the MCP auth spec, a token is bound to one tuple: resource/audience (url), OAuth mode/grant (auth_type, oauth_flow_type), the authorization-server endpoints, and the OAuth client + scopes. A shared getOAuthAuthorizationIdentity captures exactly those fields; transport (http/sse on the same url is the same audience) and delegate_auth_to_upstream (a downstream-usage toggle never sent to the authorize request) are excluded. UI: both the create and edit forms now discard the held token (React state / sessionStorage / hook, plus the fetched token + DCR client in form.credentials) whenever the identity diverges from the one it was authorized against, re-applying the admin's in-flight edit so it is never wiped. The check lives in one shared helper so the two forms cannot drift. Backend: editing an MCP server now compares the pre/post identity and, on a mint-relevant change, purges every stored per-user OAuth credential for the server (DB row + per-user token cache) so no user forwards a token minted for a resource/AS/client that no longer matches. Best-effort; a purge failure never fails the update. --- litellm/proxy/_experimental/mcp_server/db.py | 44 +++++++++ .../mcp_management_endpoints.py | 29 ++++++ .../mcp_server/test_db_credentials.py | 89 +++++++++++++++++++ .../mcp_tools/create_mcp_server.test.tsx | 38 ++++++++ .../mcp_tools/create_mcp_server.tsx | 67 +++++++------- .../components/mcp_tools/mcp_server_edit.tsx | 53 ++++++++++- .../src/components/mcp_tools/types.tsx | 27 ++++++ 7 files changed, 312 insertions(+), 35 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 10081ce19de..8c5e728d86b 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -1070,6 +1070,50 @@ async def list_user_oauth_credentials( return results +def mcp_oauth_token_identity(server: Any) -> tuple[Any, ...]: + """The upstream-OAuth-token-determining fields of an MCP server: the resource/audience (url), the + OAuth mode/grant (auth_type, oauth2_flow), the authorization-server endpoints, and the OAuth client + + scopes. Mirrors the dashboard's getOAuthAuthorizationIdentity. When any of these change on a server + update, previously stored per-user tokens were minted for the old identity and are stale. Excludes + transport and delegate_auth_to_upstream, which do not affect what token is minted (RFC 8707/8693).""" + creds = getattr(server, "credentials", None) + creds_dict: Dict[str, Any] = creds if isinstance(creds, dict) else {} + return ( + getattr(server, "url", None), + getattr(server, "auth_type", None), + getattr(server, "oauth2_flow", None), + getattr(server, "authorization_url", None), + getattr(server, "token_url", None), + getattr(server, "registration_url", None), + creds_dict.get("client_id"), + creds_dict.get("client_secret"), + creds_dict.get("scopes"), + ) + + +async def purge_user_oauth_credentials_for_server(prisma_client: PrismaClient, server_id: str) -> int: + """Delete every stored per-user OAuth credential for a server and drop each from the per-user token + cache, so no user keeps a token minted for a superseded configuration. Called when a server update + changes a mint-relevant field (see mcp_oauth_token_identity). Returns the number of rows removed.""" + repo = MCPUserCredentialsRepository(prisma_client) + rows = await repo.table.find_many(where={"server_id": server_id}) + if not rows: + return 0 + await repo.table.delete_many(where={"server_id": server_id}) + from litellm.proxy._experimental.mcp_server.oauth2_token_cache import ( + mcp_per_user_token_cache, + ) + + for row in rows: + try: + await mcp_per_user_token_cache.delete(row.user_id, server_id) + except Exception as exc: # noqa: BLE001 - cache drop is best-effort; the DB delete is authoritative + verbose_proxy_logger.warning( + "Failed to drop cached MCP OAuth token for user=%s server=%s: %s", row.user_id, server_id, exc + ) + return len(rows) + + async def refresh_user_oauth_token( prisma_client: PrismaClient, user_id: str, diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index c9952b245c7..8f6779b17b9 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -125,7 +125,9 @@ if MCP_AVAILABLE: get_user_env_vars_bulk, get_user_oauth_credential, list_user_oauth_credentials, + mcp_oauth_token_identity, merge_user_env_vars, + purge_user_oauth_credentials_for_server, reject_mcp_server, store_user_credential, store_user_oauth_credential, @@ -2318,6 +2320,9 @@ if MCP_AVAILABLE: }, ) + # Snapshot the pre-update identity so we can detect a mint-relevant change below. + old_server_record = await get_mcp_server(prisma_client, payload.server_id) + # try to update the mcp server mcp_server_record_updated = await update_mcp_server( prisma_client, @@ -2336,6 +2341,30 @@ if MCP_AVAILABLE: # Ensure registry is up to date by reloading from database await global_mcp_server_manager.reload_servers_from_database() + # If a field that determines which upstream OAuth token gets minted changed (url/audience, OAuth + # mode/grant, authorization-server endpoints, or the OAuth client + scopes), every stored per-user + # token was minted for the old configuration and is stale. Purge them (DB + cache) so the next + # tool call re-authorizes instead of forwarding a token for a resource/AS/client that no longer + # matches. Best-effort: a purge failure must not fail the update, whose primary job already + # succeeded. + if old_server_record is not None and mcp_oauth_token_identity(old_server_record) != mcp_oauth_token_identity( + mcp_server_record_updated + ): + try: + purged = await purge_user_oauth_credentials_for_server(prisma_client, payload.server_id) + if purged: + verbose_logger.info( + "MCP server %s: purged %d stale per-user OAuth token(s) after a mint-relevant config change", + payload.server_id, + purged, + ) + except Exception as exc: # noqa: BLE001 - purge is best-effort; the server update already succeeded + verbose_logger.warning( + "MCP server %s: failed to purge stale per-user OAuth tokens after config change: %s", + payload.server_id, + exc, + ) + # TODO: Enterprise: Finish audit log trail if litellm.store_audit_logs: pass diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py index 8b8b8a363d5..628f422fbf1 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py @@ -11,6 +11,7 @@ keeps a plain-base64 fallback on read so existing rows continue to work. import base64 import json from datetime import datetime, timedelta, timezone +from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock import pytest @@ -63,6 +64,94 @@ def _legacy_row(payload: str): return row +def _identity_server(**overrides): + base = dict( + url="https://up.example.com/mcp", + auth_type="oauth2", + oauth2_flow="authorization_code", + authorization_url="https://idp.example.com/authorize", + token_url="https://idp.example.com/token", + registration_url="https://idp.example.com/register", + credentials={"client_id": "cid", "client_secret": "csec", "scopes": ["a"]}, + server_name="srv", + description="d", + ) + base.update(overrides) + return SimpleNamespace(**base) + + +@pytest.mark.parametrize( + "overrides", + [ + {"url": "https://other.example.com/mcp"}, + {"auth_type": "oauth_delegate"}, + {"oauth2_flow": "client_credentials"}, + {"authorization_url": "https://other.example.com/authorize"}, + {"token_url": "https://other.example.com/token"}, + {"registration_url": "https://other.example.com/register"}, + {"credentials": {"client_id": "new", "client_secret": "csec", "scopes": ["a"]}}, + {"credentials": {"client_id": "cid", "client_secret": "rotated", "scopes": ["a"]}}, + {"credentials": {"client_id": "cid", "client_secret": "csec", "scopes": ["b"]}}, + ], +) +def test_mcp_oauth_token_identity_changes_on_mint_relevant_fields(overrides): + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + assert mcp_oauth_token_identity(_identity_server()) != mcp_oauth_token_identity(_identity_server(**overrides)) + + +@pytest.mark.parametrize( + "overrides", + [ + {"server_name": "renamed"}, + {"description": "changed"}, + ], +) +def test_mcp_oauth_token_identity_stable_on_non_mint_fields(overrides): + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + assert mcp_oauth_token_identity(_identity_server()) == mcp_oauth_token_identity(_identity_server(**overrides)) + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_deletes_rows_and_cache(monkeypatch): + from litellm.proxy._experimental.mcp_server import oauth2_token_cache + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + r1 = MagicMock(user_id="alice", server_id="srv-1") + r2 = MagicMock(user_id="bob", server_id="srv-1") + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[r1, r2]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock() + + cache_deletes = [] + monkeypatch.setattr( + oauth2_token_cache.mcp_per_user_token_cache, + "delete", + AsyncMock(side_effect=lambda uid, sid: cache_deletes.append((uid, sid))), + ) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 2 + prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once() + assert set(cache_deletes) == {("alice", "srv-1"), ("bob", "srv-1")} + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_noop_when_empty(): + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock() + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 0 + prisma.db.litellm_mcpusercredentials.delete_many.assert_not_awaited() + + def _stored_value(prisma) -> str: """Pull the credential_b64 value passed to the most recent upsert call.""" call = prisma.db.litellm_mcpusercredentials.upsert.call_args diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index eecbc253b6b..7339420ed68 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -681,6 +681,44 @@ describe("CreateMCPServer", () => { // Asserted in setupOAuthInteractive }); + it("invalidates the held token when the auth mode changes after Authorize & Fetch", async () => { + await setupOAuthInteractive(); + const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com"); + await act(async () => { + fireEvent.change(urlInput, { target: { value: "https://a.example.com/mcp" } }); + }); + act(() => { + oauthHook.onTokenReceived?.({ access_token: "tok-a" }, { clientId: "client-a", clientSecret: "secret-a" }); + }); + oauthHook.reset.mockClear(); + + // Switching the Authentication mode changes the OAuth identity, so the held token is discarded. + await selectAntOption("Authentication", "True Passthrough (no LiteLLM auth)"); + + await waitFor(() => expect(oauthHook.reset).toHaveBeenCalled()); + }); + + it("does NOT invalidate the held token when a non-mint field (server name) changes", async () => { + await setupOAuthInteractive(); + const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com"); + await act(async () => { + fireEvent.change(urlInput, { target: { value: "https://a.example.com/mcp" } }); + }); + act(() => { + oauthHook.onTokenReceived?.({ access_token: "tok-a" }, { clientId: "client-a", clientSecret: "secret-a" }); + }); + oauthHook.reset.mockClear(); + + const nameInput = document.getElementById("server_name") as HTMLInputElement; + await act(async () => { + fireEvent.change(nameInput, { target: { value: "Renamed_Server" } }); + }); + + // server_name is not part of the OAuth identity, so the held token must survive the edit. + await waitFor(() => expect(screen.getAllByRole("button", { name: "Add MCP Server" }).length).toBeGreaterThan(0)); + expect(oauthHook.reset).not.toHaveBeenCalled(); + }); + it("includes token_validation in payload when token_validation_json is filled with valid JSON", async () => { vi.mocked(networking.createMCPServer).mockResolvedValue({ server_id: "new-server-oauth", diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 0b39add234c..17c7e7c0b9a 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -15,6 +15,7 @@ import { MCP_OAUTH2_FLOW_M2M, MCP_OAUTH2_FLOW_INTERACTIVE, isClientForwardedTokenMode, + getOAuthAuthorizationIdentity, } from "./types"; import OAuthFormFields from "./OAuthFormFields"; import TruePassthroughWarning from "./TruePassthroughWarning"; @@ -99,7 +100,10 @@ const CreateMCPServer: React.FC = ({ const [oauthAccessToken, setOauthAccessToken] = useState(null); const [logoUrl, setLogoUrl] = useState(undefined); const [oauthDocsUrl, setOauthDocsUrl] = useState(null); - const [authorizedUrl, setAuthorizedUrl] = useState(undefined); + // The OAuth authorization identity (see getOAuthAuthorizationIdentity) captured at the moment a token + // was fetched; undefined when no valid token is held. If any mint-relevant field diverges from this, + // the held token is stale and is discarded so the admin must re-authorize. + const [authorizedIdentity, setAuthorizedIdentity] = useState(undefined); // Single hook call shared by MCPConnectionStatus and MCPToolConfiguration to avoid duplicate requests. const { @@ -125,12 +129,6 @@ const CreateMCPServer: React.FC = ({ const isAwsSigV4AuthType = authType === AUTH_TYPE.AWS_SIGV4; const isM2MFlow = isOAuthAuthType && formValues.oauth_flow_type === OAUTH_FLOW.M2M; - const getOAuthAuthorizationTarget = (values: Record): string | undefined => { - const transport = values.transport || transportType; - const target = transport === TRANSPORT.OPENAPI ? values.spec_path : values.url; - return typeof target === "string" ? target : undefined; - }; - const persistCreateUiState = () => { if (typeof window === "undefined") { return; @@ -207,6 +205,7 @@ const CreateMCPServer: React.FC = ({ // and committed to sessionStorage on submit; it must never be written into form.credentials, // which would persist it as server-level credentials on the created server row. Mirrors the // edit form's onTokenReceived early return. + setAuthorizedIdentity(getOAuthAuthorizationIdentity(form.getFieldsValue(true))); NotificationsManager.success( "Token held for this browser session. Tools can now be previewed and configured; nothing will be saved to LiteLLM.", ); @@ -223,7 +222,9 @@ const CreateMCPServer: React.FC = ({ }; form.setFieldsValue({ credentials }); - setAuthorizedUrl(getOAuthAuthorizationTarget(form.getFieldsValue(true))); + // Capture the identity AFTER writing the DCR'd credentials so the held token is not spuriously + // invalidated by its own credential write. + setAuthorizedIdentity(getOAuthAuthorizationIdentity(form.getFieldsValue(true))); NotificationsManager.success( "OAuth authorization successful! Please click 'Create MCP Server' to save the configuration.", @@ -233,13 +234,24 @@ const CreateMCPServer: React.FC = ({ flowSource: "create", }); - const clearAuthorizedOAuthState = (values: Record) => { - form.resetFields(["credentials", "authorization_url", "token_url", "registration_url"]); - form.setFieldsValue(values); + // Discard the held browser-authorized token and its tool preview when the authorization identity + // changes (or the modal closes). For oauth2 the fetched token + DCR client also live in + // form.credentials, and the discovered endpoints in authorization_url/token_url/registration_url, so + // those form fields are reset too; whatever the admin just changed (passed via changedValues) is + // re-applied so the invalidation never wipes their in-flight edit. + const CLEARED_ON_INVALIDATION = ["credentials", "authorization_url", "token_url", "registration_url"] as const; + const clearHeldOAuthToken = (changedValues: Record = {}) => { setOauthAccessToken(null); clearTools(); resetOAuthFlow(); - setAuthorizedUrl(undefined); + setAuthorizedIdentity(undefined); + form.resetFields([...CLEARED_ON_INVALIDATION]); + const preserved = Object.fromEntries( + CLEARED_ON_INVALIDATION.filter((key) => key in changedValues).map((key) => [key, changedValues[key]]), + ); + if (Object.keys(preserved).length > 0) { + form.setFieldsValue(preserved); + } }; React.useEffect(() => { @@ -577,7 +589,7 @@ const CreateMCPServer: React.FC = ({ : { spec_path: undefined, command: undefined, args: undefined, env: undefined }; const nextValues = - authorizedUrl === undefined + authorizedIdentity === undefined ? transportValues : { ...transportValues, @@ -587,10 +599,9 @@ const CreateMCPServer: React.FC = ({ registration_url: undefined, }; - if (authorizedUrl !== undefined) { - clearAuthorizedOAuthState(nextValues); - } else { - form.setFieldsValue(nextValues); + form.setFieldsValue(nextValues); + if (authorizedIdentity !== undefined) { + clearHeldOAuthToken(); } }; @@ -652,28 +663,18 @@ const CreateMCPServer: React.FC = ({ setOauthAccessToken(null); clearTools(); resetOAuthFlow(); - setAuthorizedUrl(undefined); + setAuthorizedIdentity(undefined); } }, [isModalVisible, form, clearTools, resetOAuthFlow]); const isAdmin = isAdminRole(userRole); const handleFormValuesChange = (changedValues: Record, allValues: Record) => { - const changedAuthorizationTarget = "url" in changedValues || "spec_path" in changedValues; - if ( - changedAuthorizationTarget && - authorizedUrl !== undefined && - getOAuthAuthorizationTarget(allValues) !== authorizedUrl - ) { - const invalidated = { - credentials: undefined, - authorization_url: changedValues.authorization_url, - token_url: changedValues.token_url, - registration_url: changedValues.registration_url, - }; - clearAuthorizedOAuthState(invalidated); - setFormValues({ ...allValues, ...invalidated }); - return; + // Any change to a mint-relevant field (url, auth_type, oauth_flow_type, client creds/scopes, or the + // authorization/token/registration endpoints — see getOAuthAuthorizationIdentity) makes a held token + // stale, so discard it and force a fresh authorize. + if (authorizedIdentity !== undefined && getOAuthAuthorizationIdentity(allValues) !== authorizedIdentity) { + clearHeldOAuthToken(changedValues); } setFormValues(allValues); }; diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index ee7938d2904..55adcf2bb59 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -5,6 +5,7 @@ import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/rea import { AUTH_TYPE, isClientForwardedTokenMode, + getOAuthAuthorizationIdentity, OAUTH_FLOW, MCP_OAUTH2_FLOW_M2M, MCP_OAUTH2_FLOW_INTERACTIVE, @@ -15,7 +16,7 @@ import { oauth2FlowToFormValue, } from "./types"; import { updateMCPServer, listMCPTools, storeMCPOAuthUserCredential } from "../networking"; -import { getToken, isTokenValid, setToken } from "@/utils/mcpTokenStore"; +import { getToken, isTokenValid, removeToken, setToken } from "@/utils/mcpTokenStore"; import { buildMcpPassthroughAuthHeader } from "@/utils/mcpHeaderUtils"; import MCPServerCostConfig from "./mcp_server_cost_config"; import MCPPermissionManagement from "./MCPPermissionManagement"; @@ -136,11 +137,17 @@ const MCPServerEdit: React.FC = ({ // that read only mcpServer.auth_type go stale the moment the admin switches modes in the form. const getEffectiveAuthType = () => form.getFieldValue("auth_type") ?? mcpServer.auth_type; + // The OAuth authorization identity (see getOAuthAuthorizationIdentity) captured when a token is fetched + // in this edit session; undefined when none is held. If a mint-relevant field later diverges from it, + // the held token (hook response + sessionStorage) is discarded so the admin must re-authorize. + const authorizedIdentityRef = React.useRef(undefined); + const { startOAuthFlow, status: oauthStatus, error: oauthError, tokenResponse: oauthTokenResponse, + reset: resetOAuthFlow, } = useMcpOAuthFlow({ accessToken, getCredentials: () => form.getFieldValue("credentials"), @@ -183,6 +190,7 @@ const MCPServerEdit: React.FC = ({ return; } + authorizedIdentityRef.current = getOAuthAuthorizationIdentity(form.getFieldsValue(true)); if (isClientForwardedTokenMode(getEffectiveAuthType())) { const browserHeldToken = { access_token: token.access_token, @@ -205,6 +213,8 @@ const MCPServerEdit: React.FC = ({ }; form.setFieldsValue({ credentials }); + // Re-capture after writing credentials so the token is not invalidated by its own credential write. + authorizedIdentityRef.current = getOAuthAuthorizationIdentity(form.getFieldsValue(true)); NotificationsManager.success( "OAuth authorization successful! Please click 'Update MCP Server' to save the credentials.", @@ -378,6 +388,39 @@ const MCPServerEdit: React.FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [mcpServer, accessToken, userID, oauthTokenResponse?.access_token]); + // Invalidate a token authorized in this edit session once any mint-relevant field diverges from the + // identity it was minted against (url, auth_type, oauth_flow_type, client creds/scopes, or the + // authorization/token/registration endpoints — see getOAuthAuthorizationIdentity). Discards the hook + // token (resetOAuthFlow, which re-runs fetchTools to prompt a fresh authorize), the sessionStorage + // token (removeToken, browser-held modes), and the fetched token/DCR client in form.credentials + the + // discovered endpoint fields; the admin's in-flight edit is re-applied so it is never wiped. Only fires + // when a token was actually authorized here (ref set), so a token already valid for the saved server on + // mount is left untouched. Driven from onValuesChange (user input only), never programmatic resets. + const CLEARED_ON_INVALIDATION = ["credentials", "authorization_url", "token_url", "registration_url"] as const; + const clearHeldOAuthToken = (changedValues: Record = {}) => { + authorizedIdentityRef.current = undefined; + if (mcpServer.server_id) { + removeToken(mcpServer.server_id, userID); + } + resetOAuthFlow(); + form.resetFields([...CLEARED_ON_INVALIDATION]); + const preserved = Object.fromEntries( + CLEARED_ON_INVALIDATION.filter((key) => key in changedValues).map((key) => [key, changedValues[key]]), + ); + if (Object.keys(preserved).length > 0) { + form.setFieldsValue(preserved); + } + }; + + const handleFormValuesChange = (changedValues: Record) => { + if ( + authorizedIdentityRef.current !== undefined && + getOAuthAuthorizationIdentity(form.getFieldsValue(true)) !== authorizedIdentityRef.current + ) { + clearHeldOAuthToken(changedValues); + } + }; + const fetchTools = async () => { if (!accessToken || !mcpServer.server_id) return; @@ -805,7 +848,13 @@ const MCPServerEdit: React.FC = ({ -
+ sse on the same url is the same audience; a transport switch +// only matters when it changes the target url, which `target` already captures), delegate_auth_to_upstream +// (a downstream-usage toggle that is never sent to the authorize request), and all metadata/RBAC/routing +// fields. Shared by the create and edit forms so their invalidation logic cannot drift. +export const getOAuthAuthorizationIdentity = (values: Record): string => { + const credentials = (values.credentials ?? {}) as Record; + const target = values.transport === TRANSPORT.OPENAPI ? values.spec_path : values.url; + const identity = { + target: typeof target === "string" ? target : null, + auth_type: values.auth_type ?? null, + oauth_flow_type: values.oauth_flow_type ?? null, + client_id: credentials.client_id ?? null, + client_secret: credentials.client_secret ?? null, + scopes: credentials.scopes ?? null, + authorization_url: values.authorization_url ?? null, + token_url: values.token_url ?? null, + registration_url: values.registration_url ?? null, + }; + return JSON.stringify(identity); +}; + // Backend value of `oauth2_flow` that marks a machine-to-machine server. Distinct // from the UI-local OAUTH_FLOW.M2M ("m2m"); this is what the API actually returns. export const MCP_OAUTH2_FLOW_M2M = "client_credentials"; From 48124734a08d40ec1b17c3de86c1af0cafc6fa97 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 12:41:15 -0700 Subject: [PATCH 082/399] fix(mcp): compare the token identity decrypted and invalidate every per-user token store Review follow-ups on the stale-token invalidation. The backend identity now decrypts client_id and client_secret before comparing: the stored values are NaCl-encrypted with a fresh nonce on every write, so comparing ciphertext flagged every routine save as a mint-relevant change and purged per-user tokens that were still valid. The identity also gains spec_path, the audience for OpenAPI servers, and parses credentials stored as a JSON string The purge now routes each (user, server) through the manager's invalidate_user_oauth_token_cache, which becomes the single invalidation point covering both the legacy per-user token cache and the v2 per-user OAuth token store; previously the purge evicted only the legacy cache while the revoke path evicted only the v2 store, so each path left the other cache serving a replaced token until its TTL. A credential row racing in between the find and the delete is now detected via the delete_many count and logged; its cache entry expires by TTL On the dashboard, CLEARED_ON_INVALIDATION and the staleness check move to types.tsx as the single shared implementation for both forms. The edit form's transport handler now rechecks the identity after its programmatic setFieldsValue calls, which antd does not report through onValuesChange, so a token no longer survives a transport switch that clears the mint target. The create form rebuilds formValues from the post-reset form state after an invalidation instead of publishing the pre-reset snapshot, so the tool preview can no longer refetch with the discarded DCR client. Both transport handlers now share the recheck, which also stops the create form from over-invalidating on an http to sse swap that keeps the same url and therefore the same audience --- litellm/proxy/_experimental/mcp_server/db.py | 76 ++++++-- .../mcp_server/mcp_server_manager.py | 17 +- .../mcp_server/test_db_credentials.py | 182 +++++++++--------- .../mcp_server/test_mcp_server_manager.py | 37 +++- .../mcp_tools/create_mcp_server.test.tsx | 50 +++++ .../mcp_tools/create_mcp_server.tsx | 32 ++- .../mcp_tools/mcp_server_edit.test.tsx | 81 +++++++- .../components/mcp_tools/mcp_server_edit.tsx | 21 +- .../src/components/mcp_tools/types.tsx | 15 ++ 9 files changed, 367 insertions(+), 144 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 8c5e728d86b..135a9e055d6 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -1070,48 +1070,82 @@ async def list_user_oauth_credentials( return results +def _decrypted_credential_field(creds: Dict[str, Any], field: str) -> Any: + """Return one credential field decrypted with the global salt key; non-string and legacy + plaintext values come back unchanged (decrypt_value_helper returns the original on failure).""" + value = creds.get(field) + if not isinstance(value, str): + return value + return decrypt_value_helper( + value=value, + key=field, + exception_type="debug", + return_original_value=True, + ) + + def mcp_oauth_token_identity(server: Any) -> tuple[Any, ...]: - """The upstream-OAuth-token-determining fields of an MCP server: the resource/audience (url), the - OAuth mode/grant (auth_type, oauth2_flow), the authorization-server endpoints, and the OAuth client + - scopes. Mirrors the dashboard's getOAuthAuthorizationIdentity. When any of these change on a server - update, previously stored per-user tokens were minted for the old identity and are stale. Excludes - transport and delegate_auth_to_upstream, which do not affect what token is minted (RFC 8707/8693).""" + """The upstream-OAuth-token-determining fields of an MCP server: the resource/audience (url, or + spec_path for OpenAPI servers), the OAuth mode/grant (auth_type, oauth2_flow), the + authorization-server endpoints, and the OAuth client + scopes. Mirrors the dashboard's + getOAuthAuthorizationIdentity. When any of these change on a server update, previously stored + per-user tokens were minted for the old identity and are stale. Excludes transport and + delegate_auth_to_upstream, which do not affect what token is minted (RFC 8707/8693). + + client_id/client_secret are compared decrypted: stored values are NaCl-encrypted with a fresh + nonce on every write, so comparing ciphertext would flag every routine save as an identity + change and purge tokens that are still valid.""" creds = getattr(server, "credentials", None) - creds_dict: Dict[str, Any] = creds if isinstance(creds, dict) else {} + if isinstance(creds, str): + try: + parsed: Any = json.loads(creds) + except ValueError: + parsed = None + else: + parsed = creds + creds_dict: Dict[str, Any] = parsed if isinstance(parsed, dict) else {} return ( getattr(server, "url", None), + getattr(server, "spec_path", None), getattr(server, "auth_type", None), getattr(server, "oauth2_flow", None), getattr(server, "authorization_url", None), getattr(server, "token_url", None), getattr(server, "registration_url", None), - creds_dict.get("client_id"), - creds_dict.get("client_secret"), + _decrypted_credential_field(creds_dict, "client_id"), + _decrypted_credential_field(creds_dict, "client_secret"), creds_dict.get("scopes"), ) async def purge_user_oauth_credentials_for_server(prisma_client: PrismaClient, server_id: str) -> int: - """Delete every stored per-user OAuth credential for a server and drop each from the per-user token - cache, so no user keeps a token minted for a superseded configuration. Called when a server update - changes a mint-relevant field (see mcp_oauth_token_identity). Returns the number of rows removed.""" + """Delete every stored per-user OAuth credential for a server and invalidate each user's cached + token everywhere it can be served from (the legacy per-user token cache and the v2 per-user OAuth + token store), so no user keeps a token minted for a superseded configuration. Called when a server + update changes a mint-relevant field (see mcp_oauth_token_identity). Returns the number of rows + removed. A row inserted between the find and the delete is removed from the DB but cannot be + evicted from the caches (its user_id was never seen); that case is detected, logged, and bounded + by the cache TTL.""" repo = MCPUserCredentialsRepository(prisma_client) rows = await repo.table.find_many(where={"server_id": server_id}) if not rows: return 0 - await repo.table.delete_many(where={"server_id": server_id}) - from litellm.proxy._experimental.mcp_server.oauth2_token_cache import ( - mcp_per_user_token_cache, + deleted_count = await repo.table.delete_many(where={"server_id": server_id}) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, ) for row in rows: - try: - await mcp_per_user_token_cache.delete(row.user_id, server_id) - except Exception as exc: # noqa: BLE001 - cache drop is best-effort; the DB delete is authoritative - verbose_proxy_logger.warning( - "Failed to drop cached MCP OAuth token for user=%s server=%s: %s", row.user_id, server_id, exc - ) - return len(rows) + await global_mcp_server_manager.invalidate_user_oauth_token_cache(row.user_id, server_id) + if deleted_count != len(rows): + verbose_proxy_logger.warning( + "MCP server %s: purge removed %d credential row(s) but %d were enumerated; " + "row(s) raced in during the purge and their cached tokens will expire by TTL", + server_id, + deleted_count, + len(rows), + ) + return deleted_count async def refresh_user_oauth_token( diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 356ed7a2729..cc4a9e63105 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -57,7 +57,10 @@ from litellm.proxy._experimental.mcp_server.elicitation_handler import ( from litellm.proxy._experimental.mcp_server.sampling_handler import ( MCP_SAMPLING_AVAILABLE, ) -from litellm.proxy._experimental.mcp_server.oauth2_token_cache import resolve_mcp_auth +from litellm.proxy._experimental.mcp_server.oauth2_token_cache import ( + mcp_per_user_token_cache, + resolve_mcp_auth, +) from litellm.proxy._experimental.mcp_server.outbound_credentials import ( Error, Ok, @@ -4053,10 +4056,13 @@ class MCPServerManager: return await self._cred_provider.has_user_token(to_subject(user_api_key_auth, None), spec) async def invalidate_user_oauth_token_cache(self, user_id: str, server_id: str) -> None: - """Drop the v2 chain's cached token for ``(user_id, server_id)`` after the credential row - changes (re-auth, revoke), so the next resolve reads the new row instead of serving the - replaced token until its cache TTL. Best-effort: a cache-drop failure is logged, never - raised, because the DB write already succeeded and the TTL remains the backstop. + """Drop every cached token for ``(user_id, server_id)`` after the credential row changes + (re-auth, revoke, config-change purge): the v2 chain's cache and the legacy per-user token + cache, so the next resolve reads the new row instead of serving the replaced token until its + cache TTL, whichever path resolves it. This is the single invalidation point for per-user + OAuth tokens; callers must not evict individual caches directly. Best-effort: a cache-drop + failure is logged, never raised, because the DB write already succeeded and the TTL remains + the backstop. """ try: await self._per_user_oauth_token_store.invalidate(user_id, server_id) @@ -4064,6 +4070,7 @@ class MCPServerManager: verbose_logger.warning( "Failed to invalidate cached MCP OAuth token for user=%s server=%s: %s", user_id, server_id, exc ) + await mcp_per_user_token_cache.delete(user_id, server_id) async def _resolve_oauth2_headers_for_tool_call( self, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py index 628f422fbf1..51641991ef9 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py @@ -84,6 +84,7 @@ def _identity_server(**overrides): "overrides", [ {"url": "https://other.example.com/mcp"}, + {"spec_path": "https://up.example.com/openapi.json"}, {"auth_type": "oauth_delegate"}, {"oauth2_flow": "client_credentials"}, {"authorization_url": "https://other.example.com/authorize"}, @@ -113,29 +114,91 @@ def test_mcp_oauth_token_identity_stable_on_non_mint_fields(overrides): assert mcp_oauth_token_identity(_identity_server()) == mcp_oauth_token_identity(_identity_server(**overrides)) +def _encrypted_creds_json(client_id: str = "cid", client_secret: str = "csec") -> str: + from litellm.proxy._experimental.mcp_server.db import encrypt_credentials + + encrypted = encrypt_credentials( + credentials={"client_id": client_id, "client_secret": client_secret, "scopes": ["a"]}, + encryption_key=None, + ) + return json.dumps(encrypted) + + +def test_mcp_oauth_token_identity_stable_across_reencryption(): + """Stored client_id/client_secret are NaCl-encrypted with a fresh nonce on every write, so two + saves of the SAME plaintext produce different ciphertext. The identity must compare decrypted + values; comparing ciphertext would flag every routine save as a mint-relevant change and purge + per-user tokens that are still valid.""" + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + first = _encrypted_creds_json() + second = _encrypted_creds_json() + assert first != second + + assert mcp_oauth_token_identity(_identity_server(credentials=first)) == mcp_oauth_token_identity( + _identity_server(credentials=second) + ) + + +def test_mcp_oauth_token_identity_detects_change_under_encryption(): + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + unchanged = _identity_server(credentials=_encrypted_creds_json()) + changed = _identity_server(credentials=_encrypted_creds_json(client_id="other")) + assert mcp_oauth_token_identity(unchanged) != mcp_oauth_token_identity(changed) + + @pytest.mark.asyncio -async def test_purge_user_oauth_credentials_for_server_deletes_rows_and_cache(monkeypatch): - from litellm.proxy._experimental.mcp_server import oauth2_token_cache +async def test_purge_user_oauth_credentials_for_server_invalidates_every_store(monkeypatch): + """The purge must route each (user, server) through the manager's shared invalidation, which is + the single point covering both the legacy per-user token cache and the v2 per-user OAuth token + store; evicting only one cache lets the other keep serving a token minted for the old config.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server r1 = MagicMock(user_id="alice", server_id="srv-1") r2 = MagicMock(user_id="bob", server_id="srv-1") prisma = MagicMock() prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[r1, r2]) - prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock() + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) - cache_deletes = [] + invalidations = [] monkeypatch.setattr( - oauth2_token_cache.mcp_per_user_token_cache, - "delete", - AsyncMock(side_effect=lambda uid, sid: cache_deletes.append((uid, sid))), + mcp_server_manager.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + AsyncMock(side_effect=lambda uid, sid: invalidations.append((uid, sid))), ) purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") assert purged == 2 prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once() - assert set(cache_deletes) == {("alice", "srv-1"), ("bob", "srv-1")} + assert set(invalidations) == {("alice", "srv-1"), ("bob", "srv-1")} + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_logs_raced_rows(monkeypatch): + from litellm.proxy._experimental.mcp_server import db as db_module + from litellm.proxy._experimental.mcp_server import mcp_server_manager + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( + return_value=[MagicMock(user_id="alice", server_id="srv-1")] + ) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) + monkeypatch.setattr( + mcp_server_manager.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + AsyncMock(), + ) + warning = MagicMock() + monkeypatch.setattr(db_module.verbose_proxy_logger, "warning", warning) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 2 + warning.assert_called_once() @pytest.mark.asyncio @@ -225,9 +288,7 @@ async def test_store_user_oauth_credential_does_not_persist_plaintext(): access_token = "ya29.a0AfH6SMBverysecretaccesstoken" prisma = _make_prisma_with_existing(row=None) - await store_user_oauth_credential( - prisma, "alice", "srv-1", access_token, refresh_token="rfr-xyz" - ) + await store_user_oauth_credential(prisma, "alice", "srv-1", access_token, refresh_token="rfr-xyz") stored = _stored_value(prisma) try: @@ -310,9 +371,7 @@ async def test_byok_guard_rejects_overwriting_encrypted_byok(): encrypted_row = MagicMock() encrypted_row.credential_b64 = _stored_value(prisma) - prisma.db.litellm_mcpusercredentials.find_unique = AsyncMock( - return_value=encrypted_row - ) + prisma.db.litellm_mcpusercredentials.find_unique = AsyncMock(return_value=encrypted_row) with pytest.raises(ValueError, match="could not be verified as an OAuth2"): await store_user_oauth_credential(prisma, "alice", "srv-1", "tok") @@ -354,18 +413,14 @@ async def test_list_oauth_credentials_filters_byok_and_returns_payloads(): "connected_at": "2024-01-01T00:00:00Z", } legacy_row = MagicMock() - legacy_row.credential_b64 = base64.urlsafe_b64encode( - json.dumps(legacy_payload).encode() - ).decode() + legacy_row.credential_b64 = base64.urlsafe_b64encode(json.dumps(legacy_payload).encode()).decode() legacy_row.server_id = "srv-legacy" byok_row = MagicMock() byok_row.credential_b64 = base64.urlsafe_b64encode(b"plain-byok-key").decode() byok_row.server_id = "srv-byok" - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[encrypted_row, legacy_row, byok_row] - ) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[encrypted_row, legacy_row, byok_row]) results = await list_user_oauth_credentials(prisma, "alice") @@ -415,9 +470,7 @@ async def test_rotate_re_encrypts_byok_with_new_key(monkeypatch): prisma.db.litellm_mcpusercredentials.update = AsyncMock() new_master_key = "rotated-salt-key-9999-9999-9999-9999" - await rotate_mcp_user_credentials_master_key( - prisma_client=prisma, new_master_key=new_master_key - ) + await rotate_mcp_user_credentials_master_key(prisma_client=prisma, new_master_key=new_master_key) update_call = prisma.db.litellm_mcpusercredentials.update.call_args new_stored = update_call.kwargs["data"]["credential_b64"] @@ -445,19 +498,13 @@ async def test_rotate_migrates_legacy_plaintext_rows(monkeypatch): legacy_row.user_id = "alice" legacy_row.server_id = "srv-legacy" legacy_row.credential_b64 = base64.urlsafe_b64encode(b"legacy-plain").decode() - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[legacy_row] - ) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[legacy_row]) prisma.db.litellm_mcpusercredentials.update = AsyncMock() new_key = "another-rotation-key-aaaa-bbbb-cccc-dddd" - await rotate_mcp_user_credentials_master_key( - prisma_client=prisma, new_master_key=new_key - ) + await rotate_mcp_user_credentials_master_key(prisma_client=prisma, new_master_key=new_key) - new_stored = prisma.db.litellm_mcpusercredentials.update.call_args.kwargs["data"][ - "credential_b64" - ] + new_stored = prisma.db.litellm_mcpusercredentials.update.call_args.kwargs["data"]["credential_b64"] monkeypatch.setenv("LITELLM_SALT_KEY", new_key) assert ( decrypt_value_helper( @@ -485,14 +532,10 @@ async def test_rotate_skips_undecodable_rows(): good_row.server_id = "srv-ok" good_row.credential_b64 = base64.urlsafe_b64encode(b"good-byok").decode() - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[bad_row, good_row] - ) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[bad_row, good_row]) prisma.db.litellm_mcpusercredentials.update = AsyncMock() - await rotate_mcp_user_credentials_master_key( - prisma_client=prisma, new_master_key="new-key-xxxx" - ) + await rotate_mcp_user_credentials_master_key(prisma_client=prisma, new_master_key="new-key-xxxx") # Only one update call — the good row. assert prisma.db.litellm_mcpusercredentials.update.call_count == 1 @@ -508,9 +551,7 @@ def _oauth_cred(access_token="at-live", refresh_token=None, expires_in_seconds=N if refresh_token is not None: cred["refresh_token"] = refresh_token if expires_in_seconds is not None: - cred["expires_at"] = ( - datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds) - ).isoformat() + cred["expires_at"] = (datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds)).isoformat() return cred @@ -527,12 +568,7 @@ def test_expiry_buffer_treats_soon_to_expire_as_expired(): cred = _oauth_cred(expires_in_seconds=30) assert is_oauth_credential_expired(cred, buffer_seconds=60) is True # A token comfortably beyond the buffer stays valid. - assert ( - is_oauth_credential_expired( - _oauth_cred(expires_in_seconds=600), buffer_seconds=60 - ) - is False - ) + assert is_oauth_credential_expired(_oauth_cred(expires_in_seconds=600), buffer_seconds=60) is False def test_expiry_past_is_expired_regardless_of_buffer(): @@ -554,9 +590,7 @@ async def test_resolve_returns_valid_token_without_refreshing(monkeypatch): refresh = AsyncMock() monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - cred = _oauth_cred( - access_token="at-live", refresh_token="rt-1", expires_in_seconds=600 - ) + cred = _oauth_cred(access_token="at-live", refresh_token="rt-1", expires_in_seconds=600) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=cred, prisma_client=MagicMock() ) @@ -572,15 +606,11 @@ async def test_resolve_refreshes_expired_token_with_refresh_token(monkeypatch): # new token rather than returning None (which left the UI tool list empty). import litellm.proxy._experimental.mcp_server.db as db_mod - refreshed = _oauth_cred( - access_token="at-fresh", refresh_token="rt-2", expires_in_seconds=3600 - ) + refreshed = _oauth_cred(access_token="at-fresh", refresh_token="rt-2", expires_in_seconds=3600) refresh = AsyncMock(return_value=refreshed) monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - expired = _oauth_cred( - access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5 - ) + expired = _oauth_cred(access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=expired, prisma_client=MagicMock() ) @@ -599,9 +629,7 @@ async def test_resolve_refreshes_token_expiring_within_buffer(monkeypatch): refresh = AsyncMock(return_value=refreshed) monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - soon = _oauth_cred( - access_token="at-soon", refresh_token="rt-1", expires_in_seconds=30 - ) + soon = _oauth_cred(access_token="at-soon", refresh_token="rt-1", expires_in_seconds=30) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=soon, prisma_client=MagicMock() ) @@ -635,9 +663,7 @@ async def test_resolve_returns_none_when_refresh_fails(monkeypatch): refresh = AsyncMock(return_value=None) monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - expired = _oauth_cred( - access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5 - ) + expired = _oauth_cred(access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=expired, prisma_client=MagicMock() ) @@ -654,9 +680,7 @@ async def test_resolve_returns_none_for_missing_credential(monkeypatch): monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) assert ( - await resolve_valid_user_oauth_token( - user_id="alice", server=MagicMock(), cred=None, prisma_client=MagicMock() - ) + await resolve_valid_user_oauth_token(user_id="alice", server=MagicMock(), cred=None, prisma_client=MagicMock()) is None ) assert ( @@ -690,19 +714,13 @@ async def test_rotate_user_env_vars_re_encrypts_with_new_key(monkeypatch): encrypted_old = encrypt_value_helper(json.dumps(values)) prisma = MagicMock() - prisma.db.litellm_mcpuserenvvars.find_many = AsyncMock( - return_value=[_env_var_row(encrypted_old)] - ) + prisma.db.litellm_mcpuserenvvars.find_many = AsyncMock(return_value=[_env_var_row(encrypted_old)]) prisma.db.litellm_mcpuserenvvars.update = AsyncMock() new_master_key = "rotated-env-key-1111-2222-3333-4444" - await rotate_mcp_user_env_vars_master_key( - prisma_client=prisma, new_master_key=new_master_key - ) + await rotate_mcp_user_env_vars_master_key(prisma_client=prisma, new_master_key=new_master_key) - new_stored = prisma.db.litellm_mcpuserenvvars.update.call_args.kwargs["data"][ - "values_b64" - ] + new_stored = prisma.db.litellm_mcpuserenvvars.update.call_args.kwargs["data"]["values_b64"] assert new_stored != encrypted_old, "rotation must produce different ciphertext" monkeypatch.setenv("LITELLM_SALT_KEY", new_master_key) @@ -719,18 +737,14 @@ async def test_rotate_user_env_vars_re_encrypts_with_new_key(monkeypatch): async def test_rotate_user_env_vars_skips_undecryptable_rows(): # A corrupt row must be skipped (not overwritten) so recoverable data is # preserved and one bad row does not abort the rest of the rotation. - good = _env_var_row( - encrypt_value_helper(json.dumps({"A": "1"})), server_id="srv-ok" - ) + good = _env_var_row(encrypt_value_helper(json.dumps({"A": "1"})), server_id="srv-ok") bad = _env_var_row("!!! not encrypted !!!", server_id="srv-corrupt") prisma = MagicMock() prisma.db.litellm_mcpuserenvvars.find_many = AsyncMock(return_value=[bad, good]) prisma.db.litellm_mcpuserenvvars.update = AsyncMock() - await rotate_mcp_user_env_vars_master_key( - prisma_client=prisma, new_master_key="new-key-xxxx" - ) + await rotate_mcp_user_env_vars_master_key(prisma_client=prisma, new_master_key="new-key-xxxx") assert prisma.db.litellm_mcpuserenvvars.update.call_count == 1 where = prisma.db.litellm_mcpuserenvvars.update.call_args.kwargs["where"] @@ -758,9 +772,7 @@ async def test_refresh_user_oauth_token_uses_client_secret_basic(monkeypatch): monkeypatch.setattr(db_mod, "get_async_httpx_client", lambda **kwargs: mock_client) monkeypatch.setattr(db_mod, "store_user_oauth_credential", AsyncMock()) - monkeypatch.setattr( - db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"}) - ) + monkeypatch.setattr(db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"})) result = await db_mod.refresh_user_oauth_token( prisma_client=MagicMock(), @@ -799,9 +811,7 @@ async def test_refresh_user_oauth_token_defaults_to_client_secret_post(monkeypat monkeypatch.setattr(db_mod, "get_async_httpx_client", lambda **kwargs: mock_client) monkeypatch.setattr(db_mod, "store_user_oauth_credential", AsyncMock()) - monkeypatch.setattr( - db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"}) - ) + monkeypatch.setattr(db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"})) await db_mod.refresh_user_oauth_token( prisma_client=MagicMock(), diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index e8fca9ac6ab..e11d78d07ae 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -3328,8 +3328,34 @@ class TestMCPServerManager: assert store.invalidations == [("alice", "srv-1")] @pytest.mark.asyncio - async def test_invalidate_user_oauth_token_cache_swallows_store_errors(self): - """A cache-drop failure must not fail the credential write that triggered it.""" + async def test_invalidate_user_oauth_token_cache_drops_legacy_cache_too(self, monkeypatch): + """A per-user token can be served from the legacy per-user token cache as well as the v2 + store; the shared invalidation must evict both, or the path not evicted keeps serving a + token minted for a replaced credential row until its TTL.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module + + class _Store: + async def fetch(self, user_id: str, server_id: str): + return None + + async def invalidate(self, user_id: str, server_id: str) -> None: + return None + + legacy_deletes: list[tuple[str, str]] = [] + monkeypatch.setattr( + manager_module.mcp_per_user_token_cache, + "delete", + AsyncMock(side_effect=lambda uid, sid: legacy_deletes.append((uid, sid))), + ) + manager = MCPServerManager(per_user_oauth_token_store=_Store()) + await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + assert legacy_deletes == [("alice", "srv-1")] + + @pytest.mark.asyncio + async def test_invalidate_user_oauth_token_cache_swallows_store_errors(self, monkeypatch): + """A cache-drop failure must not fail the credential write that triggered it, and the + legacy cache must still be evicted after the v2 store drop fails.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module class _Store: async def fetch(self, user_id: str, server_id: str): @@ -3338,8 +3364,15 @@ class TestMCPServerManager: async def invalidate(self, user_id: str, server_id: str) -> None: raise RuntimeError("redis down") + legacy_deletes: list[tuple[str, str]] = [] + monkeypatch.setattr( + manager_module.mcp_per_user_token_cache, + "delete", + AsyncMock(side_effect=lambda uid, sid: legacy_deletes.append((uid, sid))), + ) manager = MCPServerManager(per_user_oauth_token_store=_Store()) await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + assert legacy_deletes == [("alice", "srv-1")] @pytest.mark.asyncio async def test_resolve_oauth2_headers_no_user_id(self): diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index 7339420ed68..7093b7c650e 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -719,6 +719,56 @@ describe("CreateMCPServer", () => { expect(oauthHook.reset).not.toHaveBeenCalled(); }); + it("does not refetch the tool preview with a discarded token after invalidation", async () => { + // Regression: handleFormValuesChange used to publish the pre-reset antd snapshot into + // formValues after clearHeldOAuthToken, so useTestMCPConnection kept the discarded OAuth + // material (the DCR client minted for the old identity) and sent it on the next tool-preview + // request. + await setupOAuthInteractive(); + const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com"); + await act(async () => { + fireEvent.change(urlInput, { target: { value: "https://a.example.com/mcp" } }); + }); + act(() => { + oauthHook.onTokenReceived?.({ access_token: "stale-tok" }, { clientId: "client-a", clientSecret: "secret-a" }); + }); + const nameInput = document.getElementById("server_name") as HTMLInputElement; + await act(async () => { + fireEvent.change(nameInput, { target: { value: "Sync_FormValues" } }); + }); + vi.mocked(networking.testMCPToolsListRequest).mockClear(); + + await selectAntOption("Authentication", "API Key"); + + await waitFor(() => expect(vi.mocked(networking.testMCPToolsListRequest)).toHaveBeenCalled()); + for (const call of vi.mocked(networking.testMCPToolsListRequest).mock.calls) { + expect(call[1]?.credentials?.client_id).not.toBe("client-a"); + expect(call[1]?.credentials?.client_secret).not.toBe("secret-a"); + expect(call[1]?.credentials?.access_token).not.toBe("stale-tok"); + } + }); + + it("keeps the held token on an http to sse switch with the same url", async () => { + // Same url means the same resource/audience (RFC 8707): the minted token is still valid, so a + // pure transport swap between the two MCP wire protocols must not force a re-authorize. + await setupOAuthInteractive(); + const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com"); + await act(async () => { + fireEvent.change(urlInput, { target: { value: "https://a.example.com/mcp" } }); + }); + act(() => { + oauthHook.onTokenReceived?.({ access_token: "tok-a" }, { clientId: "client-a", clientSecret: "secret-a" }); + }); + oauthHook.reset.mockClear(); + + await selectAntOption("Transport Type", "Server-Sent Events (SSE)"); + + await waitFor(() => { + expect(screen.getByPlaceholderText("https://your-mcp-server.com")).toBeInTheDocument(); + }); + expect(oauthHook.reset).not.toHaveBeenCalled(); + }); + it("includes token_validation in payload when token_validation_json is filled with valid JSON", async () => { vi.mocked(networking.createMCPServer).mockResolvedValue({ server_id: "new-server-oauth", diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 17c7e7c0b9a..24b8e9eaafb 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -16,6 +16,8 @@ import { MCP_OAUTH2_FLOW_INTERACTIVE, isClientForwardedTokenMode, getOAuthAuthorizationIdentity, + CLEARED_ON_INVALIDATION, + isHeldOAuthTokenStale, } from "./types"; import OAuthFormFields from "./OAuthFormFields"; import TruePassthroughWarning from "./TruePassthroughWarning"; @@ -235,11 +237,9 @@ const CreateMCPServer: React.FC = ({ }); // Discard the held browser-authorized token and its tool preview when the authorization identity - // changes (or the modal closes). For oauth2 the fetched token + DCR client also live in - // form.credentials, and the discovered endpoints in authorization_url/token_url/registration_url, so - // those form fields are reset too; whatever the admin just changed (passed via changedValues) is + // changes (or the modal closes). The CLEARED_ON_INVALIDATION form fields (shared with the edit form + // via types.tsx) are reset too; whatever the admin just changed (passed via changedValues) is // re-applied so the invalidation never wipes their in-flight edit. - const CLEARED_ON_INVALIDATION = ["credentials", "authorization_url", "token_url", "registration_url"] as const; const clearHeldOAuthToken = (changedValues: Record = {}) => { setOauthAccessToken(null); clearTools(); @@ -588,21 +588,11 @@ const CreateMCPServer: React.FC = ({ ? { url: undefined, command: undefined, args: undefined, env: undefined } : { spec_path: undefined, command: undefined, args: undefined, env: undefined }; - const nextValues = - authorizedIdentity === undefined - ? transportValues - : { - ...transportValues, - credentials: undefined, - authorization_url: undefined, - token_url: undefined, - registration_url: undefined, - }; - - form.setFieldsValue(nextValues); - if (authorizedIdentity !== undefined) { + form.setFieldsValue(transportValues); + if (isHeldOAuthTokenStale(form.getFieldsValue(true), authorizedIdentity)) { clearHeldOAuthToken(); } + setFormValues(form.getFieldsValue(true)); }; // Generate options with existing groups and potential new group @@ -672,9 +662,13 @@ const CreateMCPServer: React.FC = ({ const handleFormValuesChange = (changedValues: Record, allValues: Record) => { // Any change to a mint-relevant field (url, auth_type, oauth_flow_type, client creds/scopes, or the // authorization/token/registration endpoints — see getOAuthAuthorizationIdentity) makes a held token - // stale, so discard it and force a fresh authorize. - if (authorizedIdentity !== undefined && getOAuthAuthorizationIdentity(allValues) !== authorizedIdentity) { + // stale, so discard it and force a fresh authorize. When that happens, formValues must be rebuilt + // from the form's post-reset state, not the pre-reset allValues snapshot: the snapshot still holds + // the discarded token in credentials, and useTestMCPConnection reads formValues for tool preview. + if (isHeldOAuthTokenStale(allValues, authorizedIdentity)) { clearHeldOAuthToken(changedValues); + setFormValues({ ...form.getFieldsValue(true), ...changedValues }); + return; } setFormValues(allValues); }; diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index d55f993b926..4f3d7b69b01 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -22,15 +22,22 @@ vi.mock("../molecules/notifications_manager", () => ({ const mockOauth: { tokenResponse: any; getTemporaryPayload: (() => Record | null) | null; -} = { tokenResponse: null, getTemporaryPayload: null }; + onTokenReceived: ((token: Record | null) => void) | null; + reset: ReturnType; +} = { tokenResponse: null, getTemporaryPayload: null, onTokenReceived: null, reset: vi.fn() }; vi.mock("@/hooks/useMcpOAuthFlow", () => ({ - useMcpOAuthFlow: (opts: { getTemporaryPayload?: () => Record | null }) => { + useMcpOAuthFlow: (opts: { + getTemporaryPayload?: () => Record | null; + onTokenReceived?: (token: Record | null) => void; + }) => { mockOauth.getTemporaryPayload = opts?.getTemporaryPayload ?? null; + mockOauth.onTokenReceived = opts?.onTokenReceived ?? null; return { startOAuthFlow: vi.fn(), status: "idle", error: null, tokenResponse: mockOauth.tokenResponse, + reset: mockOauth.reset, }; }, })); @@ -92,10 +99,12 @@ vi.mock("./mcp_tool_configuration", () => ({ const mockGetToken = vi.fn(); const mockIsTokenValid = vi.fn(); const mockSetToken = vi.fn(); +const mockRemoveToken = vi.fn(); vi.mock("@/utils/mcpTokenStore", () => ({ getToken: (...args: any[]) => mockGetToken(...args), isTokenValid: (...args: any[]) => mockIsTokenValid(...args), setToken: (...args: any[]) => mockSetToken(...args), + removeToken: (...args: unknown[]) => mockRemoveToken(...args), })); // ── fixtures ────────────────────────────────────────────────────────────────── @@ -451,6 +460,74 @@ describe("MCPServerEdit (auth type switch)", () => { }); }); +describe("MCPServerEdit OAuth token invalidation", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + const renderOAuthEdit = () => + render( + , + ); + + it("invalidates a session-authorized token when the transport switches to stdio", async () => { + // Switching to stdio clears url/auth_type via programmatic form.setFieldsValue, which antd does + // not report through onValuesChange; the explicit recheck in handleTransportChange must catch it. + // Regression: the token used to survive this switch (sessionStorage + hook state kept the old + // token minted for the http url). + renderOAuthEdit(); + + act(() => { + mockOauth.onTokenReceived?.({ access_token: "tok-1" }); + }); + mockOauth.reset.mockClear(); + + await selectAntOption("Transport Type", "Standard Input/Output (stdio)"); + + await waitFor(() => expect(mockOauth.reset).toHaveBeenCalled()); + expect(mockRemoveToken).toHaveBeenCalledWith("oauth_server_1", undefined); + }); + + it("invalidates a session-authorized token when the server URL changes", async () => { + renderOAuthEdit(); + + act(() => { + mockOauth.onTokenReceived?.({ access_token: "tok-1" }); + }); + mockOauth.reset.mockClear(); + + const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com"); + await act(async () => { + fireEvent.change(urlInput, { target: { value: "https://other.example.com/mcp" } }); + }); + + await waitFor(() => expect(mockOauth.reset).toHaveBeenCalled()); + expect(mockRemoveToken).toHaveBeenCalledWith("oauth_server_1", undefined); + }); + + it("keeps a session-authorized token on an http to sse switch with the same url", async () => { + // Same url means the same resource/audience (RFC 8707): the minted token is still valid, so a + // pure transport swap between the two MCP wire protocols must not force a re-authorize. + renderOAuthEdit(); + + act(() => { + mockOauth.onTokenReceived?.({ access_token: "tok-1" }); + }); + mockOauth.reset.mockClear(); + + await selectAntOption("Transport Type", "Server-Sent Events (SSE)"); + + expect(mockOauth.reset).not.toHaveBeenCalled(); + expect(mockRemoveToken).not.toHaveBeenCalled(); + }); +}); + describe("MCPServerEdit (tool allowlist)", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 55adcf2bb59..de3528ea6a9 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -6,6 +6,8 @@ import { AUTH_TYPE, isClientForwardedTokenMode, getOAuthAuthorizationIdentity, + CLEARED_ON_INVALIDATION, + isHeldOAuthTokenStale, OAUTH_FLOW, MCP_OAUTH2_FLOW_M2M, MCP_OAUTH2_FLOW_INTERACTIVE, @@ -392,11 +394,12 @@ const MCPServerEdit: React.FC = ({ // identity it was minted against (url, auth_type, oauth_flow_type, client creds/scopes, or the // authorization/token/registration endpoints — see getOAuthAuthorizationIdentity). Discards the hook // token (resetOAuthFlow, which re-runs fetchTools to prompt a fresh authorize), the sessionStorage - // token (removeToken, browser-held modes), and the fetched token/DCR client in form.credentials + the - // discovered endpoint fields; the admin's in-flight edit is re-applied so it is never wiped. Only fires - // when a token was actually authorized here (ref set), so a token already valid for the saved server on - // mount is left untouched. Driven from onValuesChange (user input only), never programmatic resets. - const CLEARED_ON_INVALIDATION = ["credentials", "authorization_url", "token_url", "registration_url"] as const; + // token (removeToken, browser-held modes), and the fetched token/DCR client in the shared + // CLEARED_ON_INVALIDATION form fields; the admin's in-flight edit is re-applied so it is never wiped. + // Only fires when a token was actually authorized here (ref set), so a token already valid for the + // saved server on mount is left untouched. Driven from onValuesChange for user input, plus an explicit + // recheck after programmatic setFieldsValue paths (handleTransportChange), which antd does not report + // through onValuesChange. const clearHeldOAuthToken = (changedValues: Record = {}) => { authorizedIdentityRef.current = undefined; if (mcpServer.server_id) { @@ -413,10 +416,7 @@ const MCPServerEdit: React.FC = ({ }; const handleFormValuesChange = (changedValues: Record) => { - if ( - authorizedIdentityRef.current !== undefined && - getOAuthAuthorizationIdentity(form.getFieldsValue(true)) !== authorizedIdentityRef.current - ) { + if (isHeldOAuthTokenStale(form.getFieldsValue(true), authorizedIdentityRef.current)) { clearHeldOAuthToken(changedValues); } }; @@ -539,6 +539,9 @@ const MCPServerEdit: React.FC = ({ stdio_config: undefined, }); } + if (isHeldOAuthTokenStale(form.getFieldsValue(true), authorizedIdentityRef.current)) { + clearHeldOAuthToken(); + } }; const handleSave = async (values: Record) => { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx index 7386fc98bc2..e894cf4b8dc 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx @@ -84,6 +84,21 @@ export const getOAuthAuthorizationIdentity = (values: Record): return JSON.stringify(identity); }; +// The form fields wiped when a held OAuth token is invalidated: the fetched token + DCR client live in +// `credentials`, and the three endpoint fields were discovered by the authorize flow, so all of them are +// stale together with the token. Shared by the create and edit forms so what gets wiped cannot drift. +export const CLEARED_ON_INVALIDATION = ["credentials", "authorization_url", "token_url", "registration_url"] as const; + +// True when a token was authorized in this session (authorizedIdentity recorded at mint time) and the +// form's current identity no longer matches it. Every invalidation decision in both forms goes through +// this single check: onValuesChange for user edits, and an explicit recheck after any programmatic +// form.setFieldsValue (antd does not fire onValuesChange for those), so a missed event path cannot let a +// stale token survive. +export const isHeldOAuthTokenStale = ( + values: Record, + authorizedIdentity: string | undefined, +): boolean => authorizedIdentity !== undefined && getOAuthAuthorizationIdentity(values) !== authorizedIdentity; + // Backend value of `oauth2_flow` that marks a machine-to-machine server. Distinct // from the UI-local OAUTH_FLOW.M2M ("m2m"); this is what the API actually returns. export const MCP_OAUTH2_FLOW_M2M = "client_credentials"; From 42388c3d689807f5e94de9311c40a09448bb488f Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 12:52:18 -0700 Subject: [PATCH 083/399] refactor(mcp): align the invalidation code with the v2 DI and typing discipline The purge takes an injectable invalidate_token_cache callable defaulting to the manager's shared invalidation, and MCPServerManager takes an injectable per_user_token_cache alongside the existing per_user_oauth_token_store, so tests inject fakes instead of monkeypatching the global manager and the module-level cache. The new identity helpers drop Any for object throughout --- litellm/proxy/_experimental/mcp_server/db.py | 32 +++++++++----- .../mcp_server/mcp_server_manager.py | 5 ++- .../mcp_server/test_db_credentials.py | 28 +++++-------- .../mcp_server/test_mcp_server_manager.py | 42 ++++++++++--------- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 135a9e055d6..96b28afc093 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -3,7 +3,7 @@ import binascii import hashlib import json from datetime import datetime, timedelta, timezone -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Union, cast +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Iterable, List, Optional, Set, Union, cast from litellm._logging import verbose_proxy_logger from litellm._uuid import uuid @@ -1070,7 +1070,7 @@ async def list_user_oauth_credentials( return results -def _decrypted_credential_field(creds: Dict[str, Any], field: str) -> Any: +def _decrypted_credential_field(creds: Dict[str, object], field: str) -> object: """Return one credential field decrypted with the global salt key; non-string and legacy plaintext values come back unchanged (decrypt_value_helper returns the original on failure).""" value = creds.get(field) @@ -1084,7 +1084,7 @@ def _decrypted_credential_field(creds: Dict[str, Any], field: str) -> Any: ) -def mcp_oauth_token_identity(server: Any) -> tuple[Any, ...]: +def mcp_oauth_token_identity(server: object) -> tuple[object, ...]: """The upstream-OAuth-token-determining fields of an MCP server: the resource/audience (url, or spec_path for OpenAPI servers), the OAuth mode/grant (auth_type, oauth2_flow), the authorization-server endpoints, and the OAuth client + scopes. Mirrors the dashboard's @@ -1098,12 +1098,12 @@ def mcp_oauth_token_identity(server: Any) -> tuple[Any, ...]: creds = getattr(server, "credentials", None) if isinstance(creds, str): try: - parsed: Any = json.loads(creds) + parsed: object = json.loads(creds) except ValueError: parsed = None else: parsed = creds - creds_dict: Dict[str, Any] = parsed if isinstance(parsed, dict) else {} + creds_dict: Dict[str, object] = parsed if isinstance(parsed, dict) else {} return ( getattr(server, "url", None), getattr(server, "spec_path", None), @@ -1118,25 +1118,35 @@ def mcp_oauth_token_identity(server: Any) -> tuple[Any, ...]: ) -async def purge_user_oauth_credentials_for_server(prisma_client: PrismaClient, server_id: str) -> int: +async def purge_user_oauth_credentials_for_server( + prisma_client: PrismaClient, + server_id: str, + invalidate_token_cache: Optional[Callable[[str, str], Awaitable[None]]] = None, +) -> int: """Delete every stored per-user OAuth credential for a server and invalidate each user's cached token everywhere it can be served from (the legacy per-user token cache and the v2 per-user OAuth token store), so no user keeps a token minted for a superseded configuration. Called when a server update changes a mint-relevant field (see mcp_oauth_token_identity). Returns the number of rows removed. A row inserted between the find and the delete is removed from the DB but cannot be evicted from the caches (its user_id was never seen); that case is detected, logged, and bounded - by the cache TTL.""" + by the cache TTL. + + invalidate_token_cache is injectable for tests; it defaults to the manager's shared + invalidate_user_oauth_token_cache, the single invalidation point for per-user tokens.""" repo = MCPUserCredentialsRepository(prisma_client) rows = await repo.table.find_many(where={"server_id": server_id}) if not rows: return 0 deleted_count = await repo.table.delete_many(where={"server_id": server_id}) - from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( - global_mcp_server_manager, - ) + if invalidate_token_cache is None: + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + invalidate_token_cache = global_mcp_server_manager.invalidate_user_oauth_token_cache for row in rows: - await global_mcp_server_manager.invalidate_user_oauth_token_cache(row.user_id, server_id) + await invalidate_token_cache(row.user_id, server_id) if deleted_count != len(rows): verbose_proxy_logger.warning( "MCP server %s: purge removed %d credential row(s) but %d were enumerated; " diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index cc4a9e63105..41a87a17d58 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -58,6 +58,7 @@ from litellm.proxy._experimental.mcp_server.sampling_handler import ( MCP_SAMPLING_AVAILABLE, ) from litellm.proxy._experimental.mcp_server.oauth2_token_cache import ( + MCPPerUserTokenCache, mcp_per_user_token_cache, resolve_mcp_auth, ) @@ -802,10 +803,12 @@ class MCPServerManager: self, cred_provider: Optional[UpstreamCredentialProvider] = None, per_user_oauth_token_store: Optional[InvalidatableOAuthTokenStore] = None, + per_user_token_cache: Optional[MCPPerUserTokenCache] = None, ): self._per_user_oauth_token_store = per_user_oauth_token_store or LazyPerUserOAuthTokenStore( self.get_mcp_server_by_id ) + self._per_user_token_cache = per_user_token_cache or mcp_per_user_token_cache self._cred_provider = cred_provider or UpstreamCredentialProvider( oauth_token_store=self._per_user_oauth_token_store, token_exchanger=build_token_exchanger(), @@ -4070,7 +4073,7 @@ class MCPServerManager: verbose_logger.warning( "Failed to invalidate cached MCP OAuth token for user=%s server=%s: %s", user_id, server_id, exc ) - await mcp_per_user_token_cache.delete(user_id, server_id) + await self._per_user_token_cache.delete(user_id, server_id) async def _resolve_oauth2_headers_for_tool_call( self, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py index 51641991ef9..1615d81fae9 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py @@ -149,11 +149,11 @@ def test_mcp_oauth_token_identity_detects_change_under_encryption(): @pytest.mark.asyncio -async def test_purge_user_oauth_credentials_for_server_invalidates_every_store(monkeypatch): - """The purge must route each (user, server) through the manager's shared invalidation, which is - the single point covering both the legacy per-user token cache and the v2 per-user OAuth token - store; evicting only one cache lets the other keep serving a token minted for the old config.""" - from litellm.proxy._experimental.mcp_server import mcp_server_manager +async def test_purge_user_oauth_credentials_for_server_invalidates_every_store(): + """The purge must route each (user, server) through the injected invalidator (defaulting to the + manager's shared invalidation, the single point covering both the legacy per-user token cache and + the v2 per-user OAuth token store); evicting only one cache lets the other keep serving a token + minted for the old config.""" from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server r1 = MagicMock(user_id="alice", server_id="srv-1") @@ -163,13 +163,11 @@ async def test_purge_user_oauth_credentials_for_server_invalidates_every_store(m prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) invalidations = [] - monkeypatch.setattr( - mcp_server_manager.global_mcp_server_manager, - "invalidate_user_oauth_token_cache", - AsyncMock(side_effect=lambda uid, sid: invalidations.append((uid, sid))), - ) - purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + async def record_invalidation(user_id: str, server_id: str) -> None: + invalidations.append((user_id, server_id)) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) assert purged == 2 prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once() @@ -179,7 +177,6 @@ async def test_purge_user_oauth_credentials_for_server_invalidates_every_store(m @pytest.mark.asyncio async def test_purge_user_oauth_credentials_for_server_logs_raced_rows(monkeypatch): from litellm.proxy._experimental.mcp_server import db as db_module - from litellm.proxy._experimental.mcp_server import mcp_server_manager from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server prisma = MagicMock() @@ -187,15 +184,10 @@ async def test_purge_user_oauth_credentials_for_server_logs_raced_rows(monkeypat return_value=[MagicMock(user_id="alice", server_id="srv-1")] ) prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) - monkeypatch.setattr( - mcp_server_manager.global_mcp_server_manager, - "invalidate_user_oauth_token_cache", - AsyncMock(), - ) warning = MagicMock() monkeypatch.setattr(db_module.verbose_proxy_logger, "warning", warning) - purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=AsyncMock()) assert purged == 2 warning.assert_called_once() diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index e11d78d07ae..97fdd186dda 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -3328,11 +3328,10 @@ class TestMCPServerManager: assert store.invalidations == [("alice", "srv-1")] @pytest.mark.asyncio - async def test_invalidate_user_oauth_token_cache_drops_legacy_cache_too(self, monkeypatch): + async def test_invalidate_user_oauth_token_cache_drops_legacy_cache_too(self): """A per-user token can be served from the legacy per-user token cache as well as the v2 store; the shared invalidation must evict both, or the path not evicted keeps serving a token minted for a replaced credential row until its TTL.""" - from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module class _Store: async def fetch(self, user_id: str, server_id: str): @@ -3341,21 +3340,22 @@ class TestMCPServerManager: async def invalidate(self, user_id: str, server_id: str) -> None: return None - legacy_deletes: list[tuple[str, str]] = [] - monkeypatch.setattr( - manager_module.mcp_per_user_token_cache, - "delete", - AsyncMock(side_effect=lambda uid, sid: legacy_deletes.append((uid, sid))), - ) - manager = MCPServerManager(per_user_oauth_token_store=_Store()) + class _LegacyCache: + def __init__(self) -> None: + self.deletes: list[tuple[str, str]] = [] + + async def delete(self, user_id: str, server_id: str) -> None: + self.deletes.append((user_id, server_id)) + + legacy_cache = _LegacyCache() + manager = MCPServerManager(per_user_oauth_token_store=_Store(), per_user_token_cache=legacy_cache) await manager.invalidate_user_oauth_token_cache("alice", "srv-1") - assert legacy_deletes == [("alice", "srv-1")] + assert legacy_cache.deletes == [("alice", "srv-1")] @pytest.mark.asyncio - async def test_invalidate_user_oauth_token_cache_swallows_store_errors(self, monkeypatch): + async def test_invalidate_user_oauth_token_cache_swallows_store_errors(self): """A cache-drop failure must not fail the credential write that triggered it, and the legacy cache must still be evicted after the v2 store drop fails.""" - from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module class _Store: async def fetch(self, user_id: str, server_id: str): @@ -3364,15 +3364,17 @@ class TestMCPServerManager: async def invalidate(self, user_id: str, server_id: str) -> None: raise RuntimeError("redis down") - legacy_deletes: list[tuple[str, str]] = [] - monkeypatch.setattr( - manager_module.mcp_per_user_token_cache, - "delete", - AsyncMock(side_effect=lambda uid, sid: legacy_deletes.append((uid, sid))), - ) - manager = MCPServerManager(per_user_oauth_token_store=_Store()) + class _LegacyCache: + def __init__(self) -> None: + self.deletes: list[tuple[str, str]] = [] + + async def delete(self, user_id: str, server_id: str) -> None: + self.deletes.append((user_id, server_id)) + + legacy_cache = _LegacyCache() + manager = MCPServerManager(per_user_oauth_token_store=_Store(), per_user_token_cache=legacy_cache) await manager.invalidate_user_oauth_token_cache("alice", "srv-1") - assert legacy_deletes == [("alice", "srv-1")] + assert legacy_cache.deletes == [("alice", "srv-1")] @pytest.mark.asyncio async def test_resolve_oauth2_headers_no_user_id(self): From c75184bec9b6c5337e5c810e59d34635eb340ecf Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 13:10:24 -0700 Subject: [PATCH 084/399] fix(mcp): make the pre-update identity snapshot advisory so a read failure cannot fail the edit The snapshot read only feeds the stale-token purge decision; leaving it unguarded meant a failed read would 500 an edit whose update would have succeeded, and it broke test_edit_mcp_server_redacts_credentials, whose mocked prisma is not awaitable on the un-patched get_mcp_server path. A failure now logs and skips the purge, consistent with the purge half already being best-effort. Adds the first endpoint-level coverage of the edit purge wiring: purge on a mint-relevant change, no purge when the identity is unchanged, and edit success with purge skipped when the snapshot read raises --- .../mcp_management_endpoints.py | 14 +++- .../test_mcp_management_endpoints.py | 76 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 8f6779b17b9..907a17d76d9 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -2320,8 +2320,18 @@ if MCP_AVAILABLE: }, ) - # Snapshot the pre-update identity so we can detect a mint-relevant change below. - old_server_record = await get_mcp_server(prisma_client, payload.server_id) + # Snapshot the pre-update identity so we can detect a mint-relevant change below. The read is + # advisory (it only feeds the stale-token purge decision), so a failure skips the purge with a + # warning instead of failing the edit, whose primary job is the update itself. + try: + old_server_record = await get_mcp_server(prisma_client, payload.server_id) + except Exception as exc: # noqa: BLE001 - advisory read; invalidation is best-effort end-to-end + verbose_logger.warning( + "MCP server %s: could not snapshot the pre-update record; skipping the stale-token check: %s", + payload.server_id, + exc, + ) + old_server_record = None # try to update the mcp server mcp_server_record_updated = await update_mcp_server( diff --git a/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py index 86bbce36de3..a8a8ee0fbde 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py @@ -5134,3 +5134,79 @@ def test_stamp_oauth2_flow_ignores_non_oauth2(): payload = _oauth2_create_payload(auth_type="none") mgmt_endpoints.stamp_omitted_oauth2_flow(payload) assert payload.oauth2_flow is None + + +async def _run_edit(old_record, updated_record): + from litellm.proxy.management_endpoints.mcp_management_endpoints import edit_mcp_server + + server_id = updated_record.server_id + with ( + patch("litellm.proxy.management_endpoints.mcp_management_endpoints.MCP_AVAILABLE", True), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=MagicMock(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_mcp_server", + AsyncMock(side_effect=old_record) + if isinstance(old_record, Exception) + else AsyncMock(return_value=old_record), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.update_mcp_server", + AsyncMock(return_value=updated_record), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.validate_and_normalize_mcp_server_payload", + autospec=True, + ), + patch("litellm.proxy.management_endpoints.mcp_management_endpoints.global_mcp_server_manager") as mock_manager, + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.purge_user_oauth_credentials_for_server", + AsyncMock(return_value=1), + ) as mock_purge, + ): + mock_manager.update_server = AsyncMock() + mock_manager.reload_servers_from_database = AsyncMock() + payload = UpdateMCPServerRequest(server_id=server_id, alias=updated_record.alias, url=updated_record.url) + user_auth = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + result = await edit_mcp_server(payload=payload, user_api_key_dict=user_auth) + return result, mock_purge + + +@pytest.mark.asyncio +async def test_edit_mcp_server_purges_user_tokens_on_mint_relevant_change(): + server_id = str(uuid.uuid4()) + old = generate_mock_mcp_server_db_record(server_id=server_id, url="https://old.example.com/mcp") + updated = generate_mock_mcp_server_db_record(server_id=server_id, url="https://new.example.com/mcp") + + result, mock_purge = await _run_edit(old, updated) + + assert result.server_id == server_id + mock_purge.assert_awaited_once() + assert mock_purge.await_args.args[1] == server_id + + +@pytest.mark.asyncio +async def test_edit_mcp_server_skips_purge_when_identity_unchanged(): + server_id = str(uuid.uuid4()) + old = generate_mock_mcp_server_db_record(server_id=server_id, alias="Before") + updated = generate_mock_mcp_server_db_record(server_id=server_id, alias="After") + + result, mock_purge = await _run_edit(old, updated) + + assert result.server_id == server_id + mock_purge.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_edit_mcp_server_snapshot_failure_skips_purge_but_edit_succeeds(): + """The pre-update snapshot read is advisory (it only feeds the purge decision); a read failure + must skip the stale-token check with a warning, never fail the edit itself.""" + server_id = str(uuid.uuid4()) + updated = generate_mock_mcp_server_db_record(server_id=server_id, url="https://new.example.com/mcp") + + result, mock_purge = await _run_edit(RuntimeError("db read failed"), updated) + + assert result.server_id == server_id + mock_purge.assert_not_awaited() From e720b5e25a7dfa3365fd5e32b287b906daaa4216 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 13:53:59 -0700 Subject: [PATCH 085/399] test(ui): drop the vacuous access_token assertion from the preview invalidation test The staged access token never reaches formValues (it is not a registered form field), so the assertion could not fail; the DCR client pair is the leak the test actually pins, proven by the mutation run --- .../src/components/mcp_tools/create_mcp_server.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index 7093b7c650e..658116ede1f 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -744,7 +744,6 @@ describe("CreateMCPServer", () => { for (const call of vi.mocked(networking.testMCPToolsListRequest).mock.calls) { expect(call[1]?.credentials?.client_id).not.toBe("client-a"); expect(call[1]?.credentials?.client_secret).not.toBe("secret-a"); - expect(call[1]?.credentials?.access_token).not.toBe("stale-tok"); } }); From aa351311c0c7edb7f3d52df7a11a9c4c49af0cd9 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 14:33:39 -0700 Subject: [PATCH 086/399] fix(mcp): spare BYOK rows when purging stale OAuth tokens and invalidate caches on server delete LiteLLM_MCPUserCredentials stores BYOK API keys in the same column as per-user OAuth tokens, so the purge on a mint-relevant config change now deletes only rows whose payload decodes as an OAuth2 credential, each by its (user_id, server_id) pair, instead of every row for the server. An api_key server whose url changes purges nothing. delete_mcp_server now also invalidates each enumerated user's cached token so a re-created server reusing the id cannot serve tokens minted for the deleted one, and both cache drops are best-effort --- litellm/proxy/_experimental/mcp_server/db.py | 64 ++++++-- .../mcp_server/mcp_server_manager.py | 7 +- .../mcp_server/test_db_credentials.py | 143 ++++++++++++++++-- .../mcp_server/test_mcp_server.py | 1 + .../mcp_server/test_mcp_server_manager.py | 19 +++ .../test_mcp_management_endpoints.py | 18 ++- 6 files changed, 222 insertions(+), 30 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 96b28afc093..c6b7620b649 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -558,7 +558,11 @@ async def delete_mcp_server_from_virtualkey(): pass -async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Optional[LiteLLM_MCPServerTable]: +async def delete_mcp_server( + prisma_client: PrismaClient, + server_id: str, + invalidate_token_cache: Optional[Callable[[str, str], Awaitable[None]]] = None, +) -> Optional[LiteLLM_MCPServerTable]: """ Delete the mcp server from the db by server_id @@ -569,6 +573,12 @@ async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Opti caller-visible error. Each table is cleaned independently so a failure on one still attempts the other. + Each enumerated credential row's user also gets their cached per-user token + invalidated (legacy cache + v2 store, via invalidate_token_cache, defaulting + to the manager's shared invalidation): the caches are keyed by + (user_id, server_id), so without this a re-created server reusing the same + server_id would serve tokens minted for the deleted server until TTL. + Returns the deleted mcp server record if it exists, otherwise None """ deleted_server = await MCPServerRepository(prisma_client).table.delete( @@ -577,6 +587,18 @@ async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Opti }, ) if deleted_server is not None: + credential_user_ids: List[str] = [] + try: + credential_rows = await prisma_client.db.litellm_mcpusercredentials.find_many( + where={"server_id": server_id} + ) + credential_user_ids = [row.user_id for row in credential_rows] + except Exception as e: # noqa: BLE001 - enumeration is best-effort; cached tokens expire by TTL + verbose_proxy_logger.warning( + "MCP server %s deleted but per-user credential enumeration failed; cached tokens expire by TTL: %s", + server_id, + e, + ) for model, label in ( (prisma_client.db.litellm_mcpusercredentials, "credential"), (prisma_client.db.litellm_mcpuserenvvars, "env var"), @@ -591,6 +613,15 @@ async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Opti label, e, ) + if credential_user_ids: + if invalidate_token_cache is None: + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + invalidate_token_cache = global_mcp_server_manager.invalidate_user_oauth_token_cache + for user_id in credential_user_ids: + await invalidate_token_cache(user_id, server_id) return deleted_server @@ -1123,21 +1154,30 @@ async def purge_user_oauth_credentials_for_server( server_id: str, invalidate_token_cache: Optional[Callable[[str, str], Awaitable[None]]] = None, ) -> int: - """Delete every stored per-user OAuth credential for a server and invalidate each user's cached + """Delete every stored per-user OAuth token for a server and invalidate each user's cached token everywhere it can be served from (the legacy per-user token cache and the v2 per-user OAuth token store), so no user keeps a token minted for a superseded configuration. Called when a server update changes a mint-relevant field (see mcp_oauth_token_identity). Returns the number of rows - removed. A row inserted between the find and the delete is removed from the DB but cannot be - evicted from the caches (its user_id was never seen); that case is detected, logged, and bounded - by the cache TTL. + removed. + + LiteLLM_MCPUserCredentials also stores BYOK API keys in the same column; only rows whose payload + decodes as an OAuth2 credential (see _decode_oauth_payload) are deleted, because a config change + only invalidates minted tokens, never a user's own stored key. Rows are therefore deleted per + (user_id, server_id) pair rather than by a blanket server_id filter. An OAuth row inserted while + the purge runs for a user not yet enumerated survives; a re-auth completing in the window for an + already-enumerated user is deleted along with the stale row (the pair delete cannot tell them + apart), which costs that user one extra re-auth and nothing else. invalidate_token_cache is injectable for tests; it defaults to the manager's shared invalidate_user_oauth_token_cache, the single invalidation point for per-user tokens.""" repo = MCPUserCredentialsRepository(prisma_client) rows = await repo.table.find_many(where={"server_id": server_id}) - if not rows: + oauth_rows = [row for row in rows if _decode_oauth_payload(row.credential_b64) is not None] + if not oauth_rows: return 0 - deleted_count = await repo.table.delete_many(where={"server_id": server_id}) + deleted_count = sum( + [await repo.table.delete_many(where={"user_id": row.user_id, "server_id": server_id}) for row in oauth_rows] + ) if invalidate_token_cache is None: from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( global_mcp_server_manager, @@ -1145,15 +1185,15 @@ async def purge_user_oauth_credentials_for_server( invalidate_token_cache = global_mcp_server_manager.invalidate_user_oauth_token_cache - for row in rows: + for row in oauth_rows: await invalidate_token_cache(row.user_id, server_id) - if deleted_count != len(rows): + if deleted_count != len(oauth_rows): verbose_proxy_logger.warning( - "MCP server %s: purge removed %d credential row(s) but %d were enumerated; " - "row(s) raced in during the purge and their cached tokens will expire by TTL", + "MCP server %s: purge removed %d OAuth credential row(s) but %d were enumerated; " + "row(s) were deleted concurrently during the purge", server_id, deleted_count, - len(rows), + len(oauth_rows), ) return deleted_count diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 41a87a17d58..8dd9949e17b 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -4073,7 +4073,12 @@ class MCPServerManager: verbose_logger.warning( "Failed to invalidate cached MCP OAuth token for user=%s server=%s: %s", user_id, server_id, exc ) - await self._per_user_token_cache.delete(user_id, server_id) + try: + await self._per_user_token_cache.delete(user_id, server_id) + except Exception as exc: # noqa: BLE001 - cache drop is best-effort; TTL is the backstop + verbose_logger.warning( + "Failed to drop legacy cached MCP OAuth token for user=%s server=%s: %s", user_id, server_id, exc + ) async def _resolve_oauth2_headers_for_tool_call( self, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py index 1615d81fae9..0bdcffe1530 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py @@ -148,19 +148,30 @@ def test_mcp_oauth_token_identity_detects_change_under_encryption(): assert mcp_oauth_token_identity(unchanged) != mcp_oauth_token_identity(changed) +def _oauth_row(user_id: str, server_id: str = "srv-1"): + """A stored per-user OAuth token row (payload tagged type=oauth2, legacy plain-base64 encoding).""" + row = _legacy_row(json.dumps({"type": "oauth2", "access_token": "tok-" + user_id})) + row.user_id = user_id + row.server_id = server_id + return row + + +def _byok_row(user_id: str, server_id: str = "srv-1"): + """A stored BYOK API key row: the same column, but the payload is a plain string, not OAuth JSON.""" + row = _legacy_row("sk-byok-" + user_id) + row.user_id = user_id + row.server_id = server_id + return row + + @pytest.mark.asyncio -async def test_purge_user_oauth_credentials_for_server_invalidates_every_store(): - """The purge must route each (user, server) through the injected invalidator (defaulting to the - manager's shared invalidation, the single point covering both the legacy per-user token cache and - the v2 per-user OAuth token store); evicting only one cache lets the other keep serving a token - minted for the old config.""" +async def test_purge_user_oauth_credentials_for_server_invalidates_each_user(): + """The purge must route each (user, server) row through the invalidator exactly once.""" from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server - r1 = MagicMock(user_id="alice", server_id="srv-1") - r2 = MagicMock(user_id="bob", server_id="srv-1") prisma = MagicMock() - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[r1, r2]) - prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice"), _oauth_row("bob")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=1) invalidations = [] @@ -170,29 +181,131 @@ async def test_purge_user_oauth_credentials_for_server_invalidates_every_store() purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) assert purged == 2 - prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once() + assert prisma.db.litellm_mcpusercredentials.delete_many.await_count == 2 assert set(invalidations) == {("alice", "srv-1"), ("bob", "srv-1")} +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_spares_byok_rows(): + """Regression: the purge used to delete_many on server_id alone, wiping BYOK API keys that share + the LiteLLM_MCPUserCredentials table. Only rows holding an OAuth2 payload may be deleted, each by + its (user_id, server_id) pair, and only their users' token caches invalidated.""" + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_byok_row("carol"), _oauth_row("alice")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=1) + + invalidations = [] + + async def record_invalidation(user_id: str, server_id: str) -> None: + invalidations.append((user_id, server_id)) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) + + assert purged == 1 + prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once_with( + where={"user_id": "alice", "server_id": "srv-1"} + ) + assert invalidations == [("alice", "srv-1")] + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_all_byok_is_noop(): + """An api_key (BYOK-only) server whose identity tuple changes (e.g. its url) must purge nothing.""" + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_byok_row("carol"), _byok_row("dave")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock() + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 0 + prisma.db.litellm_mcpusercredentials.delete_many.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_defaults_to_manager_invalidator(monkeypatch): + """When no invalidator is injected, the purge must resolve to the manager's shared + invalidate_user_oauth_token_cache, the single point covering both the legacy per-user token cache + and the v2 per-user OAuth token store; a wrong or no-op default silently leaves every cache + serving tokens minted for the superseded config.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=1) + + shared_invalidator = AsyncMock() + monkeypatch.setattr( + mcp_server_manager.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + shared_invalidator, + ) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 1 + shared_invalidator.assert_awaited_once_with("alice", "srv-1") + + @pytest.mark.asyncio async def test_purge_user_oauth_credentials_for_server_logs_raced_rows(monkeypatch): from litellm.proxy._experimental.mcp_server import db as db_module from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server prisma = MagicMock() - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[MagicMock(user_id="alice", server_id="srv-1")] - ) - prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=0) warning = MagicMock() monkeypatch.setattr(db_module.verbose_proxy_logger, "warning", warning) purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=AsyncMock()) - assert purged == 2 + assert purged == 0 warning.assert_called_once() +@pytest.mark.asyncio +async def test_delete_mcp_server_invalidates_cached_tokens_for_enumerated_users(): + """Deleting a server must invalidate each enumerated user's cached per-user token: the caches are + keyed by (user_id, server_id), so a re-created server reusing the same server_id would otherwise + serve tokens minted for the deleted server until TTL.""" + from litellm.proxy._experimental.mcp_server.db import delete_mcp_server + + prisma = MagicMock() + prisma.db.litellm_mcpservertable.delete = AsyncMock(return_value=MagicMock(server_id="srv-1")) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice"), _byok_row("bob")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) + prisma.db.litellm_mcpuserenvvars.delete_many = AsyncMock(return_value=0) + + invalidations = [] + + async def record_invalidation(user_id: str, server_id: str) -> None: + invalidations.append((user_id, server_id)) + + deleted = await delete_mcp_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) + + assert deleted is not None + assert set(invalidations) == {("alice", "srv-1"), ("bob", "srv-1")} + + +@pytest.mark.asyncio +async def test_delete_mcp_server_returns_none_without_cleanup_when_server_missing(): + from litellm.proxy._experimental.mcp_server.db import delete_mcp_server + + prisma = MagicMock() + prisma.db.litellm_mcpservertable.delete = AsyncMock(return_value=None) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock() + + deleted = await delete_mcp_server(prisma, "srv-1", invalidate_token_cache=AsyncMock()) + + assert deleted is None + prisma.db.litellm_mcpusercredentials.find_many.assert_not_awaited() + + @pytest.mark.asyncio async def test_purge_user_oauth_credentials_for_server_noop_when_empty(): from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index bba0eb31cfb..7d25e0ba493 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -6869,6 +6869,7 @@ async def test_call_tool_with_legacy_db_m2m_server_resolves_oauth2_flow(): (None, None), ("", None), ("not a url", None), + ("http://[::1", None), ], ) def test_redact_mcp_resource_url_strips_credentials(url, expected): diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index 97fdd186dda..a18e1ac2c44 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -3376,6 +3376,25 @@ class TestMCPServerManager: await manager.invalidate_user_oauth_token_cache("alice", "srv-1") assert legacy_cache.deletes == [("alice", "srv-1")] + @pytest.mark.asyncio + async def test_invalidate_user_oauth_token_cache_swallows_legacy_cache_errors(self): + """The legacy cache drop is best-effort like the v2 drop: a failure must be logged, never + raised into the credential write that triggered the invalidation.""" + + class _Store: + async def fetch(self, user_id: str, server_id: str): + return None + + async def invalidate(self, user_id: str, server_id: str) -> None: + return None + + class _RaisingLegacyCache: + async def delete(self, user_id: str, server_id: str) -> None: + raise RuntimeError("redis down") + + manager = MCPServerManager(per_user_oauth_token_store=_Store(), per_user_token_cache=_RaisingLegacyCache()) + await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + @pytest.mark.asyncio async def test_resolve_oauth2_headers_no_user_id(self): """Skip lookup entirely when user_api_key_auth has no user_id.""" diff --git a/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py index a8a8ee0fbde..a02acc02502 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py @@ -5136,7 +5136,7 @@ def test_stamp_oauth2_flow_ignores_non_oauth2(): assert payload.oauth2_flow is None -async def _run_edit(old_record, updated_record): +async def _run_edit(old_record, updated_record, purge_mock=None): from litellm.proxy.management_endpoints.mcp_management_endpoints import edit_mcp_server server_id = updated_record.server_id @@ -5163,7 +5163,7 @@ async def _run_edit(old_record, updated_record): patch("litellm.proxy.management_endpoints.mcp_management_endpoints.global_mcp_server_manager") as mock_manager, patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.purge_user_oauth_credentials_for_server", - AsyncMock(return_value=1), + purge_mock if purge_mock is not None else AsyncMock(return_value=1), ) as mock_purge, ): mock_manager.update_server = AsyncMock() @@ -5199,6 +5199,20 @@ async def test_edit_mcp_server_skips_purge_when_identity_unchanged(): mock_purge.assert_not_awaited() +@pytest.mark.asyncio +async def test_edit_mcp_server_purge_failure_does_not_fail_the_edit(): + """The purge is best-effort: a purge exception after a successful update must be swallowed and + logged, never turned into an error response for an edit whose primary job already succeeded.""" + server_id = str(uuid.uuid4()) + old = generate_mock_mcp_server_db_record(server_id=server_id, url="https://old.example.com/mcp") + updated = generate_mock_mcp_server_db_record(server_id=server_id, url="https://new.example.com/mcp") + + result, mock_purge = await _run_edit(old, updated, purge_mock=AsyncMock(side_effect=RuntimeError("db down"))) + + assert result.server_id == server_id + mock_purge.assert_awaited_once() + + @pytest.mark.asyncio async def test_edit_mcp_server_snapshot_failure_skips_purge_but_edit_succeeds(): """The pre-update snapshot read is advisory (it only feeds the purge decision); a read failure From b304620311b3f449b84d37d20ebdc84cf8d4cb20 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 14:33:51 -0700 Subject: [PATCH 087/399] fix(ui): compare url and spec_path independently in the OAuth authorization identity The identity used to pick the audience from spec_path only when values.transport was OPENAPI, but the create form keeps transport in component state rather than form values, so spec_path edits on OpenAPI servers never invalidated a held token. Comparing url and spec_path independently mirrors the backend's mcp_oauth_token_identity and fires regardless of whether transport is present. Invalidation now also wipes only credentials; the admin-typed endpoint fields are kept --- .../mcp_tools/create_mcp_server.tsx | 3 +- .../mcp_tools/mcp_server_edit.test.tsx | 27 ++++++++++++++ .../src/components/mcp_tools/types.test.tsx | 27 ++++++++++++++ .../src/components/mcp_tools/types.tsx | 35 +++++++++++-------- 4 files changed, 77 insertions(+), 15 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 24b8e9eaafb..eb48fd02474 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -239,7 +239,8 @@ const CreateMCPServer: React.FC = ({ // Discard the held browser-authorized token and its tool preview when the authorization identity // changes (or the modal closes). The CLEARED_ON_INVALIDATION form fields (shared with the edit form // via types.tsx) are reset too; whatever the admin just changed (passed via changedValues) is - // re-applied so the invalidation never wipes their in-flight edit. + // re-applied so the invalidation never wipes their in-flight edit. Admin-typed endpoint fields are + // left alone (see CLEARED_ON_INVALIDATION). const clearHeldOAuthToken = (changedValues: Record = {}) => { setOauthAccessToken(null); clearTools(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index 4f3d7b69b01..e5daf90e992 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -511,6 +511,33 @@ describe("MCPServerEdit OAuth token invalidation", () => { expect(mockRemoveToken).toHaveBeenCalledWith("oauth_server_1", undefined); }); + it("keeps the admin's in-flight endpoint edits when the token is invalidated", async () => { + // Regression: invalidation used to form.resetFields the endpoint fields; with the edit Form's + // initialValues that silently reverted an admin-corrected token_url back to the saved (wrong) + // value while still looking plausible. Only credentials (the minted material) may be wiped. + renderOAuthEdit(); + + const tokenUrlInput = screen.getByPlaceholderText("https://example.com/oauth/token"); + await act(async () => { + fireEvent.change(tokenUrlInput, { target: { value: "https://corrected.example.com/token" } }); + }); + + act(() => { + mockOauth.onTokenReceived?.({ access_token: "tok-1" }); + }); + mockOauth.reset.mockClear(); + + const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com"); + await act(async () => { + fireEvent.change(urlInput, { target: { value: "https://moved.example.com/mcp" } }); + }); + + await waitFor(() => expect(mockOauth.reset).toHaveBeenCalled()); + expect((screen.getByPlaceholderText("https://example.com/oauth/token") as HTMLInputElement).value).toBe( + "https://corrected.example.com/token", + ); + }); + it("keeps a session-authorized token on an http to sse switch with the same url", async () => { // Same url means the same resource/audience (RFC 8707): the minted token is still valid, so a // pure transport swap between the two MCP wire protocols must not force a re-authorize. diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.test.tsx index 846bcfc9e0b..c6faca1fb51 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.test.tsx @@ -7,9 +7,36 @@ import { handleTransport, handleAuth, getMcpOAuthMode, + getOAuthAuthorizationIdentity, + isHeldOAuthTokenStale, oauth2FlowToFormValue, } from "./types"; +describe("getOAuthAuthorizationIdentity", () => { + // Regression: the identity used to pick the audience from spec_path only when values.transport was + // OPENAPI, but the create form keeps transport in component state, so values.transport was absent and + // spec_path edits on OpenAPI servers never invalidated a held token. + it("changes when spec_path changes even when transport is absent from form values", () => { + const authorized = { auth_type: AUTH_TYPE.OAUTH2, spec_path: "https://a.example.com/openapi.json" }; + const edited = { auth_type: AUTH_TYPE.OAUTH2, spec_path: "https://b.example.com/openapi.json" }; + expect(getOAuthAuthorizationIdentity(edited)).not.toBe(getOAuthAuthorizationIdentity(authorized)); + expect(isHeldOAuthTokenStale(edited, getOAuthAuthorizationIdentity(authorized))).toBe(true); + }); + + it("changes when url changes", () => { + const authorized = { auth_type: AUTH_TYPE.OAUTH2, url: "https://a.example.com/mcp" }; + const edited = { auth_type: AUTH_TYPE.OAUTH2, url: "https://b.example.com/mcp" }; + expect(getOAuthAuthorizationIdentity(edited)).not.toBe(getOAuthAuthorizationIdentity(authorized)); + }); + + it("is stable across non-mint fields", () => { + const authorized = { auth_type: AUTH_TYPE.OAUTH2, url: "https://a.example.com/mcp", server_name: "one" }; + const renamed = { auth_type: AUTH_TYPE.OAUTH2, url: "https://a.example.com/mcp", server_name: "two" }; + expect(getOAuthAuthorizationIdentity(renamed)).toBe(getOAuthAuthorizationIdentity(authorized)); + expect(isHeldOAuthTokenStale(renamed, getOAuthAuthorizationIdentity(authorized))).toBe(false); + }); +}); + describe("handleTransport", () => { it("should default to SSE when transport is null", () => { expect(handleTransport(null)).toBe(TRANSPORT.SSE); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx index e894cf4b8dc..3eba8b30968 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/types.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/types.tsx @@ -58,20 +58,24 @@ export const OAUTH_FLOW = { }; // The fields that determine which upstream OAuth token "Authorize & Fetch" mints: the resource/audience -// (url), the OAuth mode/grant (auth_type, oauth_flow_type), the OAuth client and requested scope -// (credentials.client_id / client_secret / scopes), and the authorization-server endpoints -// (authorization_url / token_url / registration_url). Grounded in RFC 8707 / RFC 8693 and the MCP auth -// spec: an access token is bound to exactly this tuple (resource/audience + scope + client + issuer), so +// (url, or spec_path for OpenAPI servers), the OAuth mode/grant (auth_type, oauth_flow_type), the OAuth +// client and requested scope (credentials.client_id / client_secret / scopes), and the authorization-server +// endpoints (authorization_url / token_url / registration_url). Grounded in RFC 8707 / RFC 8693 and the MCP +// auth spec: an access token is bound to exactly this tuple (resource/audience + scope + client + issuer), so // a previously authorized token is stale if and only if this identity changes and must be re-minted. -// Deliberately EXCLUDES: transport (http<->sse on the same url is the same audience; a transport switch -// only matters when it changes the target url, which `target` already captures), delegate_auth_to_upstream -// (a downstream-usage toggle that is never sent to the authorize request), and all metadata/RBAC/routing -// fields. Shared by the create and edit forms so their invalidation logic cannot drift. +// url and spec_path are compared independently rather than selected by transport: the create form keeps +// transport in component state, not in form values, so a transport-conditional target would silently pin the +// audience to a missing url and never fire for spec_path edits on OpenAPI servers. Mirrors the backend's +// mcp_oauth_token_identity. Deliberately EXCLUDES: transport itself (http<->sse on the same url is the same +// audience; a switch to/from OpenAPI shows up as url/spec_path changes because each form clears the field the +// new transport does not use), delegate_auth_to_upstream (a downstream-usage toggle that is never sent to the +// authorize request), and all metadata/RBAC/routing fields. Shared by the create and edit forms so their +// invalidation logic cannot drift. export const getOAuthAuthorizationIdentity = (values: Record): string => { const credentials = (values.credentials ?? {}) as Record; - const target = values.transport === TRANSPORT.OPENAPI ? values.spec_path : values.url; const identity = { - target: typeof target === "string" ? target : null, + url: typeof values.url === "string" ? values.url : null, + spec_path: typeof values.spec_path === "string" ? values.spec_path : null, auth_type: values.auth_type ?? null, oauth_flow_type: values.oauth_flow_type ?? null, client_id: credentials.client_id ?? null, @@ -84,10 +88,13 @@ export const getOAuthAuthorizationIdentity = (values: Record): return JSON.stringify(identity); }; -// The form fields wiped when a held OAuth token is invalidated: the fetched token + DCR client live in -// `credentials`, and the three endpoint fields were discovered by the authorize flow, so all of them are -// stale together with the token. Shared by the create and edit forms so what gets wiped cannot drift. -export const CLEARED_ON_INVALIDATION = ["credentials", "authorization_url", "token_url", "registration_url"] as const; +// The form fields wiped when a held OAuth token is invalidated: only `credentials`, which holds the +// minted material (the fetched token + DCR client). The authorization/token/registration endpoint +// fields are deliberately NOT wiped: nothing programmatic ever writes them (upstream discovery happens +// backend-side), so they only ever hold admin input, and resetting them would wipe it (create) or +// silently revert it to the saved record (edit, whose Form has initialValues). Shared by the create and +// edit forms so what gets wiped cannot drift. +export const CLEARED_ON_INVALIDATION = ["credentials"] as const; // True when a token was authorized in this session (authorizedIdentity recorded at mint time) and the // form's current identity no longer matches it. Every invalidation decision in both forms goes through From 71e0491d37cde6568ca90dca31368d835322bad4 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 15:06:43 -0700 Subject: [PATCH 088/399] fix(ui): preview tools with a staged interactive OAuth token in the edit form For authorization_code the edit preview listed tools by server_id only, relying on the stored per-user DB credential, so a token authorized in the edit session gave an empty preview until the admin saved; the create form previews the identical state through the config-based preview endpoint, which takes the token explicitly. The edit fetch now routes through that same endpoint when a staged interactive token is held, built from the form values with the saved record as fallback, and keeps the by-server_id listing for every other case --- .../mcp_tools/mcp_server_edit.test.tsx | 25 +++++++++ .../components/mcp_tools/mcp_server_edit.tsx | 53 ++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index e5daf90e992..9a9db2eeb95 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -10,6 +10,7 @@ vi.mock("../networking", () => ({ updateMCPServer: vi.fn(), listMCPTools: vi.fn().mockResolvedValue({ tools: [], error: null }), storeMCPOAuthUserCredential: vi.fn().mockResolvedValue({}), + testMCPToolsListRequest: vi.fn().mockResolvedValue({ tools: [], error: null }), })); vi.mock("../molecules/notifications_manager", () => ({ @@ -511,6 +512,30 @@ describe("MCPServerEdit OAuth token invalidation", () => { expect(mockRemoveToken).toHaveBeenCalledWith("oauth_server_1", undefined); }); + it("previews tools with a staged interactive OAuth token before it is saved", async () => { + // Regression: for authorization_code the fetch went by server_id only, relying on the stored DB + // credential, so a token authorized in this edit session gave an empty preview until the admin + // saved; the create form previews the identical state via the config-based preview endpoint. + mockOauth.tokenResponse = { access_token: "staged-obo-tok" }; + + renderOAuthEdit(); + + await waitFor(() => { + expect(vi.mocked(networking.testMCPToolsListRequest)).toHaveBeenCalledWith( + "access-token", + expect.objectContaining({ server_id: "oauth_server_1", url: "https://example.com/mcp" }), + "staged-obo-tok", + ); + }); + expect(networking.listMCPTools).not.toHaveBeenCalled(); + // Previewing must stay stateless: the staged token is committed only by an explicit Save + // (storeMCPOAuthUserCredential for authorization_code, setToken for the client-forwarded modes). + expect(networking.storeMCPOAuthUserCredential).not.toHaveBeenCalled(); + expect(mockSetToken).not.toHaveBeenCalled(); + expect(networking.updateMCPServer).not.toHaveBeenCalled(); + mockOauth.tokenResponse = null; + }); + it("keeps the admin's in-flight endpoint edits when the token is invalidated", async () => { // Regression: invalidation used to form.resetFields the endpoint fields; with the edit Form's // initialValues that silently reverted an admin-corrected token_url back to the saved (wrong) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index de3528ea6a9..58f63e12019 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -17,7 +17,7 @@ import { getMcpOAuthMode, oauth2FlowToFormValue, } from "./types"; -import { updateMCPServer, listMCPTools, storeMCPOAuthUserCredential } from "../networking"; +import { updateMCPServer, listMCPTools, storeMCPOAuthUserCredential, testMCPToolsListRequest } from "../networking"; import { getToken, isTokenValid, removeToken, setToken } from "@/utils/mcpTokenStore"; import { buildMcpPassthroughAuthHeader } from "@/utils/mcpHeaderUtils"; import MCPServerCostConfig from "./mcp_server_cost_config"; @@ -421,6 +421,53 @@ const MCPServerEdit: React.FC = ({ } }; + // A token authorized in this edit session for interactive OAuth (authorization_code) is only + // committed to the DB on save, so a plain by-server_id listing cannot use it and the preview would + // stay empty until the admin saves; the create form previews the identical state through the + // config-based preview endpoint, which takes the staged token explicitly. Returns false when there + // is no staged interactive token so fetchTools falls through to the by-server_id listing. + const previewWithStagedInteractiveToken = async ( + isPassthrough: boolean, + isBrowserHeldTokenMode: boolean, + ): Promise => { + const stagedToken = + !isPassthrough && !isBrowserHeldTokenMode && getEffectiveAuthType() === AUTH_TYPE.OAUTH2 + ? oauthTokenResponse?.access_token + : undefined; + if (!stagedToken) { + return false; + } + setIsLoadingTools(true); + setToolsError(null); + try { + const values = form.getFieldsValue(true); + const rawTransport = values.transport || mcpServer.transport; + const previewConfig = { + server_id: mcpServer.server_id, + server_name: values.server_name || mcpServer.server_name || mcpServer.alias, + url: values.url || mcpServer.url, + transport: rawTransport === TRANSPORT.OPENAPI ? TRANSPORT.HTTP : rawTransport, + auth_type: AUTH_TYPE.OAUTH2, + authorization_url: values.authorization_url, + token_url: values.token_url, + registration_url: values.registration_url, + }; + const toolsResponse = await testMCPToolsListRequest(accessToken, previewConfig, stagedToken); + if (toolsResponse.tools && !toolsResponse.error) { + setTools(toolsResponse.tools); + } else { + setTools([]); + setToolsError(toolsResponse.message || "Failed to load tools"); + } + } catch (error) { + setTools([]); + setToolsError(error instanceof Error ? error.message : "Failed to load tools"); + } finally { + setIsLoadingTools(false); + } + return true; + }; + const fetchTools = async () => { if (!accessToken || !mcpServer.server_id) return; @@ -436,6 +483,10 @@ const MCPServerEdit: React.FC = ({ delegate_auth_to_upstream: mcpServer.delegate_auth_to_upstream, }) === "passthrough"; const isBrowserHeldTokenMode = isClientForwardedTokenMode(getEffectiveAuthType()); + + if (await previewWithStagedInteractiveToken(isPassthrough, isBrowserHeldTokenMode)) { + return; + } if (isPassthrough || isBrowserHeldTokenMode) { const token = oauthTokenResponse?.access_token ?? From 4786e599b0f78b4d7ad4be96782b24202404c5ed Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 15:28:39 -0700 Subject: [PATCH 089/399] test(ui): pin the client-forwarded token contract on create and edit The create and edit submit paths for true_passthrough and oauth_delegate persist only the tool configuration: the parametrized create test authorizes, disables the allowlist, and asserts nothing is persisted before submit, then that the create payload carries allowed_tools but no credentials and no occurrence of the token anywhere in the serialized payload, no per-user DB credential is written, and the token is committed to sessionStorage only, keyed to the created server. The edit save test gains the same serialized-payload assertion --- .../mcp_tools/create_mcp_server.test.tsx | 62 +++++++++++++++++++ .../mcp_tools/mcp_server_edit.test.tsx | 1 + 2 files changed, 63 insertions(+) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index 658116ede1f..02374a3ffa4 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -374,6 +374,68 @@ describe("CreateMCPServer", () => { expect(credentials.access_token).toBeUndefined(); }); + it.each([ + ["true_passthrough", "True Passthrough (no LiteLLM auth)"], + ["oauth_delegate", "OAuth Delegate (client-supplied upstream token)"], + ])("persists only tool config on create for %s; the token stays browser-held", async (_authType, optionLabel) => { + oauthHook.tokenResponse = { access_token: "upstream-tok", token_type: "Bearer" }; + await selectHttpTransport(); + + const user = userEvent.setup({ delay: null }); + await user.type(getServerNameInput(), "CF_Server"); + await user.type(screen.getByPlaceholderText("https://your-mcp-server.com"), "https://example.com/mcp"); + + await selectAntOption("Authentication", optionLabel); + + await waitFor(() => expect(oauthHook.onTokenReceived).toBeTruthy()); + await act(async () => { + oauthHook.onTokenReceived!({ access_token: "upstream-tok", token_type: "Bearer" }, undefined); + }); + + fireEvent.click(screen.getByRole("button", { name: "Disable all tools" })); + + // Previewing and configuring must stay stateless: nothing is persisted anywhere (server row, + // per-user DB credential, sessionStorage) until the admin submits. + expect(networking.createMCPServer).not.toHaveBeenCalled(); + expect(networking.storeMCPOAuthUserCredential).not.toHaveBeenCalled(); + expect(setToken).not.toHaveBeenCalled(); + + const createdServer = { + server_id: "new-cf-server", + server_name: "CF_Server", + alias: "CF_Server", + url: "https://example.com/mcp", + transport: "http", + auth_type: _authType, + created_at: "2024-01-01T00:00:00Z", + created_by: "user-1", + updated_at: "2024-01-01T00:00:00Z", + updated_by: "user-1", + }; + vi.mocked(networking.createMCPServer).mockResolvedValue(createdServer); + + const submitButton = screen.getByRole("button", { name: "Add MCP Server" }); + await act(async () => { + fireEvent.click(submitButton); + }); + + await waitFor(() => expect(networking.createMCPServer).toHaveBeenCalledTimes(1)); + const [, payload] = vi.mocked(networking.createMCPServer).mock.calls[0]; + + // Only the tool configuration persists on the server row; the upstream token appears nowhere + // in the create payload and no per-user DB credential is written. The token is committed to + // sessionStorage only, keyed to the created server. + expect(payload.allowed_tools).toEqual([]); + expect(payload.credentials).toBeUndefined(); + expect(JSON.stringify(payload)).not.toContain("upstream-tok"); + expect(networking.storeMCPOAuthUserCredential).not.toHaveBeenCalled(); + expect(setToken).toHaveBeenCalledWith( + "new-cf-server", + expect.objectContaining({ access_token: "upstream-tok" }), + undefined, + ); + }); + it("should not show auth value field when None auth type is selected", async () => { await selectHttpTransport(); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index 9a9db2eeb95..93ff1333cd8 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -1339,6 +1339,7 @@ describe("MCPServerEdit (OAuth token persistence on save)", () => { expect(networking.storeMCPOAuthUserCredential).not.toHaveBeenCalled(); const [, payload] = vi.mocked(networking.updateMCPServer).mock.calls[0]; expect(payload.credentials).toBeUndefined(); + expect(JSON.stringify(payload)).not.toContain("cf-tok"); }, ); From c46c9d46526077139c7c864d886950ac40c912e6 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 16:04:19 -0700 Subject: [PATCH 090/399] docs(mcp): mcp_server_resource docstring matches the origin-only redaction The field doc still said scheme + host + path while the redactor now strips the path along with userinfo, query, and fragment, since hosted MCP servers routinely embed the credential in the path --- litellm/types/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 6b99cfa3314..c093c213e50 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -2533,9 +2533,10 @@ class StandardLoggingMCPToolCall(TypedDict, total=False): mcp_server_resource: Optional[str] """ - The upstream MCP server resource identifier (scheme + host + path) the tool call was - forwarded to. Redacted for logging: userinfo, query string, and fragment are stripped so an - upstream URL carrying an embedded token or secret query parameter never reaches log metadata. + The origin (scheme + host + port) of the upstream MCP server the tool call was forwarded + to. Redacted for logging: userinfo, the path, the query string, and the fragment are all + stripped, because hosted MCP servers routinely embed the credential in the URL path and + this value is readable by callers via request logs. Records which upstream received a relayed request; never a credential. """ From 9dcc21cd48aaee6650e2f8063692d5d1b78a1d41 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 16:18:25 -0700 Subject: [PATCH 091/399] refactor(mcp): batch the purge row deletion into one query The per-row delete_many loop becomes a single delete filtered to the enumerated OAuth users' (user_id IN, server_id) pairs; same rows deleted, same BYOK-sparing precision, same count-mismatch detection, one round-trip instead of N --- litellm/proxy/_experimental/mcp_server/db.py | 4 ++-- .../_experimental/mcp_server/test_db_credentials.py | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index c6b7620b649..e4f8b0c331d 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -1175,8 +1175,8 @@ async def purge_user_oauth_credentials_for_server( oauth_rows = [row for row in rows if _decode_oauth_payload(row.credential_b64) is not None] if not oauth_rows: return 0 - deleted_count = sum( - [await repo.table.delete_many(where={"user_id": row.user_id, "server_id": server_id}) for row in oauth_rows] + deleted_count = await repo.table.delete_many( + where={"server_id": server_id, "user_id": {"in": [row.user_id for row in oauth_rows]}} ) if invalidate_token_cache is None: from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py index 0bdcffe1530..7269774442b 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py @@ -171,7 +171,7 @@ async def test_purge_user_oauth_credentials_for_server_invalidates_each_user(): prisma = MagicMock() prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice"), _oauth_row("bob")]) - prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=1) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) invalidations = [] @@ -181,15 +181,17 @@ async def test_purge_user_oauth_credentials_for_server_invalidates_each_user(): purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) assert purged == 2 - assert prisma.db.litellm_mcpusercredentials.delete_many.await_count == 2 + prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once_with( + where={"server_id": "srv-1", "user_id": {"in": ["alice", "bob"]}} + ) assert set(invalidations) == {("alice", "srv-1"), ("bob", "srv-1")} @pytest.mark.asyncio async def test_purge_user_oauth_credentials_for_server_spares_byok_rows(): """Regression: the purge used to delete_many on server_id alone, wiping BYOK API keys that share - the LiteLLM_MCPUserCredentials table. Only rows holding an OAuth2 payload may be deleted, each by - its (user_id, server_id) pair, and only their users' token caches invalidated.""" + the LiteLLM_MCPUserCredentials table. Only rows holding an OAuth2 payload may be deleted (one + batched query filtered to their user_ids), and only their users' token caches invalidated.""" from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server prisma = MagicMock() @@ -205,7 +207,7 @@ async def test_purge_user_oauth_credentials_for_server_spares_byok_rows(): assert purged == 1 prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once_with( - where={"user_id": "alice", "server_id": "srv-1"} + where={"server_id": "srv-1", "user_id": {"in": ["alice"]}} ) assert invalidations == [("alice", "srv-1")] From db8c872c7d3cf374b143e1785edc3e8c98194f06 Mon Sep 17 00:00:00 2001 From: Tin Date: Thu, 9 Jul 2026 16:26:30 -0700 Subject: [PATCH 092/399] fix(ui): staged edit preview sends explicit oauth2_flow and spec_path; invalidation clears the tool list The preview endpoint infers client_credentials when the inherited client_id, client_secret, and token_url are all present (common once DCR or discovery filled them) and then strips the forwarded bearer to preview as M2M, so the staged interactive token was silently unused; sending oauth2_flow=authorization_code bypasses the inference. spec_path now rides along so OpenAPI servers take the spec-based preview path the create form gets. clearHeldOAuthToken also empties the tool list, mirroring the create form's clearTools, so a preview fetched with the discarded token never lingers while the refetch is in flight --- .../mcp_tools/mcp_server_edit.test.tsx | 36 ++++++++++++++++++- .../components/mcp_tools/mcp_server_edit.tsx | 7 ++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx index 93ff1333cd8..adb3e161da5 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.test.tsx @@ -523,7 +523,13 @@ describe("MCPServerEdit OAuth token invalidation", () => { await waitFor(() => { expect(vi.mocked(networking.testMCPToolsListRequest)).toHaveBeenCalledWith( "access-token", - expect.objectContaining({ server_id: "oauth_server_1", url: "https://example.com/mcp" }), + // oauth2_flow must be explicit: the preview endpoint infers client_credentials from + // inherited client_id/client_secret/token_url and would strip the staged bearer. + expect.objectContaining({ + server_id: "oauth_server_1", + url: "https://example.com/mcp", + oauth2_flow: "authorization_code", + }), "staged-obo-tok", ); }); @@ -536,6 +542,34 @@ describe("MCPServerEdit OAuth token invalidation", () => { mockOauth.tokenResponse = null; }); + it("previews an OpenAPI server's staged token against its spec_path", async () => { + mockOauth.tokenResponse = { access_token: "staged-obo-tok" }; + + render( + , + ); + + await waitFor(() => { + expect(vi.mocked(networking.testMCPToolsListRequest)).toHaveBeenCalledWith( + "access-token", + expect.objectContaining({ spec_path: "https://example.com/openapi.json" }), + "staged-obo-tok", + ); + }); + mockOauth.tokenResponse = null; + }); + it("keeps the admin's in-flight endpoint edits when the token is invalidated", async () => { // Regression: invalidation used to form.resetFields the endpoint fields; with the edit Form's // initialValues that silently reverted an admin-corrected token_url back to the saved (wrong) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 58f63e12019..7446c96c40e 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -405,6 +405,7 @@ const MCPServerEdit: React.FC = ({ if (mcpServer.server_id) { removeToken(mcpServer.server_id, userID); } + setTools([]); resetOAuthFlow(); form.resetFields([...CLEARED_ON_INVALIDATION]); const preserved = Object.fromEntries( @@ -442,12 +443,18 @@ const MCPServerEdit: React.FC = ({ try { const values = form.getFieldsValue(true); const rawTransport = values.transport || mcpServer.transport; + // oauth2_flow must be explicit: the preview endpoint infers client_credentials from the + // inherited client_id/client_secret/token_url (common once DCR or discovery filled them) and + // would strip the staged bearer to preview as M2M. spec_path keeps OpenAPI servers on the + // spec-based preview path, mirroring the create form's config. const previewConfig = { server_id: mcpServer.server_id, server_name: values.server_name || mcpServer.server_name || mcpServer.alias, url: values.url || mcpServer.url, + spec_path: values.spec_path || mcpServer.spec_path, transport: rawTransport === TRANSPORT.OPENAPI ? TRANSPORT.HTTP : rawTransport, auth_type: AUTH_TYPE.OAUTH2, + oauth2_flow: MCP_OAUTH2_FLOW_INTERACTIVE, authorization_url: values.authorization_url, token_url: values.token_url, registration_url: values.registration_url, From 1b96bfacec0542ce123922f2b3450958a1ba18c8 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 16:58:43 -0700 Subject: [PATCH 093/399] refactor(ui): widen Key ID and Created By columns, drop Last Active info icon Follow-up polish on the migrated Team Info virtual keys table: widen the Key ID column by 20px (100 -> 120), nearly double Created By (70 -> 130) so the name and popover fit, and remove the Last Active header info icon (and its now unused InfoCircleOutlined import). --- .../components/team/TeamVirtualKeysTable.tsx | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index 909608d630f..778b82a8fc5 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -5,7 +5,6 @@ import { DataTable, DataTablePagination, DataTableSortHeader } from "@/component import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; import { ColumnDef, PaginationState, SortingState } from "@tanstack/react-table"; import { Badge, Icon, Text } from "@tremor/react"; -import { InfoCircleOutlined } from "@ant-design/icons"; import { Popover, Tooltip, Typography } from "antd"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; import React, { useCallback, useEffect, useMemo, useState } from "react"; @@ -193,7 +192,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi id: "token", accessorKey: "token", header: ({ column }) => , - size: 100, + size: 120, enableSorting: true, cell: (info) => ( setSelectedKey(info.row.original)} /> @@ -283,7 +282,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi id: "created_by", accessorKey: "created_by", header: "Created By", - size: 70, + size: 130, enableSorting: false, cell: (info) => { const userId = info.getValue() as string | null; @@ -349,17 +348,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi { id: "last_active", accessorKey: "last_active", - header: () => ( - - Last Active - - - - - ), + header: "Last Active", size: 130, enableSorting: false, cell: (info) => , From 1612df18a6fdbc3a3f6cb2eabbdf105cd2c4b14e Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 17:19:21 -0700 Subject: [PATCH 094/399] refactor(ui): reuse exported DEFAULT_PAGE_SIZE_OPTIONS in DataTable Drop the duplicate local DEFAULT_PAGE_SIZE_OPTIONS in DataTable.tsx and import the one already exported from DataTablePagination.tsx, removing the divergence risk if the canonical list changes. --- .../src/components/shared/DataTable/DataTable.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx index 7655454f381..2e95ee170fa 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx @@ -36,11 +36,9 @@ import { import { cn } from "@/lib/cva.config"; import "./columnMeta"; -import { DataTablePagination } from "./DataTablePagination"; +import { DataTablePagination, DEFAULT_PAGE_SIZE_OPTIONS } from "./DataTablePagination"; import type { ColumnPinnedSide, DataTableProps, DataTableSize, PaginationMode, SortingMode } from "./types"; -const DEFAULT_PAGE_SIZE_OPTIONS = [25, 50, 100]; - const INTERACTIVE_SELECTOR = "button, a, input, select, textarea, [role=checkbox], [data-row-click-exempt]"; const noop = () => {}; From 65d90fd5cfbf1d5690708973948b989d9cbfbb1f Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Thu, 9 Jul 2026 17:43:33 -0700 Subject: [PATCH 095/399] refactor(ui): colocate 11 route segments' components into _components/ (#32704) Colocation follow-up to the App Router migration: move each page's owned components out of the shared src/components dump and into its route segment's _components/ folder, draining the shared bucket. Convention: a component used by exactly one segment goes in that segment's _components/ (private, matching Next's _ route-exclusion); a component shared by 2+ segments stays in @/components. No new _shared/ folder. Rename-in-place (segment already had a local components/ folder): - api-reference (also relocates the shared CodeBlock, used by playground and cost-tracking, to @/components/CodeBlock) - memory, budgets, access-groups - caching, projects, guardrails-monitor Extract from src/components (page view lived in the shared dump): - AdminPanel -> admin-panel, organizations -> organizations, general_settings -> router-settings, usage -> old-usage Each folder/view was verified to have no importer other than its own page (cross-checked across src, tests, and e2e_tests). Relative imports inside moved single files are rewritten to absolute @/components/*; colocated tests move with their subject and have their vi.mock paths rewritten to match. Grandfathered lint suppressions (tremor, react-hooks, and similar, all pre-existing) are re-keyed to the new paths with counts unchanged. No behavior change. --- ui/litellm-dashboard/eslint-suppressions.json | 48 +++++++++---------- .../AccessGroupsDetailsPage.test.tsx | 0 .../AccessGroupsDetailsPage.tsx | 0 .../AccessGroupsModal/AccessGroupBaseForm.tsx | 0 .../AccessGroupCreateModal.tsx | 0 .../AccessGroupEditModal.tsx | 0 .../AccessGroupsPage.test.tsx | 0 .../AccessGroupsPage.tsx | 0 .../{components => _components}/types.ts | 0 .../app/(dashboard)/access-groups/page.tsx | 2 +- .../_components}/AdminPanel.test.tsx | 14 +++--- .../admin-panel/_components}/AdminPanel.tsx | 24 +++++----- .../src/app/(dashboard)/admin-panel/page.tsx | 2 +- .../APIReferenceView.test.tsx | 2 +- .../{ => _components}/APIReferenceView.tsx | 4 +- .../{components => _components}/DocLink.tsx | 0 .../app/(dashboard)/api-reference/page.tsx | 2 +- .../budget_modal.tsx | 0 .../budget_panel.test.tsx | 0 .../budget_panel.tsx | 0 .../{components => _components}/constants.ts | 0 .../edit_budget_modal.tsx | 0 .../src/app/(dashboard)/budgets/page.tsx | 2 +- .../cache_dashboard.tsx | 0 .../cache_health.tsx | 0 .../cache_settings/CacheFieldSection.tsx | 0 .../cache_settings/CacheFormField.tsx | 0 .../cache_settings/RedisTypeSelector.test.tsx | 0 .../cache_settings/RedisTypeSelector.tsx | 0 .../cache_settings/cacheSettingsFields.ts | 0 .../cache_settings/cacheSettingsUtils.test.ts | 0 .../cache_settings/cacheSettingsUtils.ts | 0 .../cache_settings/index.test.tsx | 0 .../cache_settings/index.tsx | 0 .../response_time_indicator.tsx | 0 .../src/app/(dashboard)/caching/page.tsx | 2 +- .../components/how_it_works.test.tsx | 2 +- .../cost-tracking/components/how_it_works.tsx | 2 +- .../EvaluationSettingsModal.tsx | 0 .../GuardrailConfig.test.tsx | 0 .../GuardrailConfig.tsx | 0 .../GuardrailDetail.tsx | 0 .../GuardrailsMonitorView.test.tsx | 0 .../GuardrailsMonitorView.tsx | 0 .../GuardrailsOverview.tsx | 0 .../ScoreChart.test.tsx | 0 .../ScoreChart.tsx | 0 .../(dashboard)/guardrails-monitor/page.tsx | 2 +- .../MemoryEditModal.tsx | 0 .../MemoryView.tsx | 0 .../src/app/(dashboard)/memory/page.tsx | 2 +- .../old-usage/_components}/usage.tsx | 10 ++-- .../src/app/(dashboard)/old-usage/page.tsx | 2 +- .../_components}/organizations.test.tsx | 4 +- .../_components}/organizations.tsx | 25 ++++++---- .../app/(dashboard)/organizations/page.tsx | 2 +- .../components/chat_ui/AgentBuilderView.tsx | 2 +- .../ProjectDetailsPage.test.tsx | 0 .../ProjectDetailsPage.tsx | 0 .../ProjectKeysSection.test.tsx | 0 .../ProjectKeysSection.tsx | 0 .../ProjectKeysTable.test.tsx | 0 .../ProjectKeysTable.tsx | 0 .../ProjectModals/CreateProjectModal.test.tsx | 0 .../ProjectModals/CreateProjectModal.tsx | 0 .../ProjectModals/EditProjectModal.test.tsx | 0 .../ProjectModals/EditProjectModal.tsx | 0 .../ProjectModals/ProjectBaseForm.test.tsx | 0 .../ProjectModals/ProjectBaseForm.tsx | 0 .../ProjectModals/projectFormUtils.test.ts | 0 .../ProjectModals/projectFormUtils.ts | 0 .../ProjectsPage.test.tsx | 0 .../ProjectsPage.tsx | 0 .../src/app/(dashboard)/projects/page.tsx | 2 +- .../_components}/general_settings.tsx | 8 ++-- .../app/(dashboard)/router-settings/page.tsx | 2 +- .../components/CodeBlock.tsx | 0 .../tests/CreateKeyPage.expiredToken.test.tsx | 8 ++-- 78 files changed, 91 insertions(+), 84 deletions(-) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsDetailsPage.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsDetailsPage.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsModal/AccessGroupBaseForm.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsModal/AccessGroupCreateModal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsModal/AccessGroupEditModal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsPage.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/AccessGroupsPage.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/access-groups/{components => _components}/types.ts (100%) rename ui/litellm-dashboard/src/{components => app/(dashboard)/admin-panel/_components}/AdminPanel.test.tsx (96%) rename ui/litellm-dashboard/src/{components => app/(dashboard)/admin-panel/_components}/AdminPanel.tsx (93%) rename ui/litellm-dashboard/src/app/(dashboard)/api-reference/{ => _components}/APIReferenceView.test.tsx (97%) rename ui/litellm-dashboard/src/app/(dashboard)/api-reference/{ => _components}/APIReferenceView.tsx (97%) rename ui/litellm-dashboard/src/app/(dashboard)/api-reference/{components => _components}/DocLink.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/budgets/{components => _components}/budget_modal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/budgets/{components => _components}/budget_panel.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/budgets/{components => _components}/budget_panel.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/budgets/{components => _components}/constants.ts (100%) rename ui/litellm-dashboard/src/app/(dashboard)/budgets/{components => _components}/edit_budget_modal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_dashboard.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_health.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/CacheFieldSection.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/CacheFormField.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/RedisTypeSelector.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/RedisTypeSelector.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/cacheSettingsFields.ts (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/cacheSettingsUtils.test.ts (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/cacheSettingsUtils.ts (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/index.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/cache_settings/index.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/caching/{components => _components}/response_time_indicator.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/EvaluationSettingsModal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/GuardrailConfig.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/GuardrailConfig.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/GuardrailDetail.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/GuardrailsMonitorView.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/GuardrailsMonitorView.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/GuardrailsOverview.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/ScoreChart.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/{components => _components}/ScoreChart.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/memory/{components => _components}/MemoryEditModal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/memory/{components => _components}/MemoryView.tsx (100%) rename ui/litellm-dashboard/src/{components => app/(dashboard)/old-usage/_components}/usage.tsx (99%) rename ui/litellm-dashboard/src/{components => app/(dashboard)/organizations/_components}/organizations.test.tsx (87%) rename ui/litellm-dashboard/src/{components => app/(dashboard)/organizations/_components}/organizations.tsx (96%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectDetailsPage.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectDetailsPage.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectKeysSection.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectKeysSection.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectKeysTable.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectKeysTable.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/CreateProjectModal.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/CreateProjectModal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/EditProjectModal.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/EditProjectModal.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/ProjectBaseForm.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/ProjectBaseForm.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/projectFormUtils.test.ts (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectModals/projectFormUtils.ts (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectsPage.test.tsx (100%) rename ui/litellm-dashboard/src/app/(dashboard)/projects/{components => _components}/ProjectsPage.tsx (100%) rename ui/litellm-dashboard/src/{components => app/(dashboard)/router-settings/_components}/general_settings.tsx (96%) rename ui/litellm-dashboard/src/{app/(dashboard)/api-reference => }/components/CodeBlock.tsx (100%) diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index fe8f182c106..b490cf71768 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -4,32 +4,32 @@ "count": 1 } }, - "src/app/(dashboard)/api-reference/APIReferenceView.tsx": { + "src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/budgets/components/budget_modal.tsx": { + "src/app/(dashboard)/budgets/_components/budget_modal.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/budgets/components/budget_panel.test.tsx": { + "src/app/(dashboard)/budgets/_components/budget_panel.test.tsx": { "unused-imports/no-unused-imports": { "count": 2 } }, - "src/app/(dashboard)/budgets/components/budget_panel.tsx": { + "src/app/(dashboard)/budgets/_components/budget_panel.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/budgets/components/edit_budget_modal.tsx": { + "src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/caching/components/cache_dashboard.tsx": { + "src/app/(dashboard)/caching/_components/cache_dashboard.tsx": { "no-restricted-imports": { "count": 1 }, @@ -40,17 +40,17 @@ "count": 2 } }, - "src/app/(dashboard)/caching/components/cache_health.tsx": { + "src/app/(dashboard)/caching/_components/cache_health.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx": { + "src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/caching/components/cache_settings/index.tsx": { + "src/app/(dashboard)/caching/_components/cache_settings/index.tsx": { "no-restricted-imports": { "count": 1 }, @@ -136,32 +136,32 @@ "count": 2 } }, - "src/app/(dashboard)/guardrails-monitor/components/EvaluationSettingsModal.tsx": { + "src/app/(dashboard)/guardrails-monitor/_components/EvaluationSettingsModal.tsx": { "react-hooks/set-state-in-effect": { "count": 1 } }, - "src/app/(dashboard)/guardrails-monitor/components/GuardrailDetail.tsx": { + "src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.tsx": { "no-nested-ternary": { "count": 3 } }, - "src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx": { + "src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/guardrails-monitor/components/GuardrailsOverview.tsx": { + "src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx": { "no-nested-ternary": { "count": 8 } }, - "src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx": { + "src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx": { "react/display-name": { "count": 1 } }, - "src/app/(dashboard)/guardrails-monitor/components/ScoreChart.tsx": { + "src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx": { "no-restricted-imports": { "count": 1 } @@ -326,7 +326,7 @@ "count": 2 } }, - "src/app/(dashboard)/memory/components/MemoryView.tsx": { + "src/app/(dashboard)/memory/_components/MemoryView.tsx": { "react-hooks/set-state-in-effect": { "count": 1 } @@ -522,7 +522,7 @@ "count": 1 } }, - "src/app/(dashboard)/projects/components/ProjectDetailsPage.tsx": { + "src/app/(dashboard)/projects/_components/ProjectDetailsPage.tsx": { "no-nested-ternary": { "count": 3 }, @@ -530,17 +530,17 @@ "count": 1 } }, - "src/app/(dashboard)/projects/components/ProjectKeysSection.tsx": { + "src/app/(dashboard)/projects/_components/ProjectKeysSection.tsx": { "react-hooks/set-state-in-effect": { "count": 1 } }, - "src/app/(dashboard)/projects/components/ProjectModals/ProjectBaseForm.tsx": { + "src/app/(dashboard)/projects/_components/ProjectModals/ProjectBaseForm.tsx": { "react-hooks/set-state-in-effect": { "count": 2 } }, - "src/app/(dashboard)/projects/components/ProjectsPage.tsx": { + "src/app/(dashboard)/projects/_components/ProjectsPage.tsx": { "react-hooks/set-state-in-effect": { "count": 1 } @@ -851,7 +851,7 @@ "count": 1 } }, - "src/components/AdminPanel.tsx": { + "src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx": { "no-restricted-imports": { "count": 1 }, @@ -1520,7 +1520,7 @@ "count": 1 } }, - "src/components/general_settings.tsx": { + "src/app/(dashboard)/router-settings/_components/general_settings.tsx": { "no-nested-ternary": { "count": 3 }, @@ -2028,7 +2028,7 @@ "count": 1 } }, - "src/components/organizations.tsx": { + "src/app/(dashboard)/organizations/_components/organizations.tsx": { "no-restricted-imports": { "count": 1 } @@ -2371,7 +2371,7 @@ "count": 1 } }, - "src/components/usage.tsx": { + "src/app/(dashboard)/old-usage/_components/usage.tsx": { "no-restricted-imports": { "count": 2 }, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupBaseForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupBaseForm.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupCreateModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupCreateModal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupCreateModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupCreateModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupEditModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupEditModal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupEditModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupEditModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/types.ts b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/types.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/types.ts rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/types.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx index ae4712b826e..4e9f7031c6d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { AccessGroupsPage } from "./components/AccessGroupsPage"; +import { AccessGroupsPage } from "./_components/AccessGroupsPage"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function AccessGroups() { diff --git a/ui/litellm-dashboard/src/components/AdminPanel.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.test.tsx similarity index 96% rename from ui/litellm-dashboard/src/components/AdminPanel.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.test.tsx index 7d1d2f46cf1..220db23338e 100644 --- a/ui/litellm-dashboard/src/components/AdminPanel.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.test.tsx @@ -8,34 +8,34 @@ const mockGetAllowedIPs = vi.fn(); const mockAddAllowedIP = vi.fn(); const mockDeleteAllowedIP = vi.fn(); -vi.mock("./networking", () => ({ +vi.mock("@/components/networking", () => ({ getSSOSettings: (...args: unknown[]) => mockGetSSOSettings(...args), getAllowedIPs: (...args: unknown[]) => mockGetAllowedIPs(...args), addAllowedIP: (...args: unknown[]) => mockAddAllowedIP(...args), deleteAllowedIP: (...args: unknown[]) => mockDeleteAllowedIP(...args), })); -vi.mock("./constants", () => ({ +vi.mock("@/components/constants", () => ({ useBaseUrl: () => "http://localhost:4000", })); -vi.mock("./Settings/AdminSettings/SSOSettings/SSOSettings", () => ({ +vi.mock("@/components/Settings/AdminSettings/SSOSettings/SSOSettings", () => ({ default: () =>
SSO Settings
, })); -vi.mock("./Settings/AdminSettings/UISettings/UISettings", () => ({ +vi.mock("@/components/Settings/AdminSettings/UISettings/UISettings", () => ({ default: () =>
UI Settings
, })); -vi.mock("./SCIM", () => ({ +vi.mock("@/components/SCIM", () => ({ default: () =>
SCIM Config
, })); -vi.mock("./SSOModals", () => ({ +vi.mock("@/components/SSOModals", () => ({ default: () =>
SSO Modals
, })); -vi.mock("./UIAccessControlForm", () => ({ +vi.mock("@/components/UIAccessControlForm", () => ({ default: () =>
UI Access Control Form
, })); diff --git a/ui/litellm-dashboard/src/components/AdminPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx similarity index 93% rename from ui/litellm-dashboard/src/components/AdminPanel.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx index 7867c184ed2..611efd6a588 100644 --- a/ui/litellm-dashboard/src/components/AdminPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx @@ -16,18 +16,18 @@ import { } from "@tremor/react"; import { Alert, Button as Button2, Form, Input, Modal, Space, Tabs, Typography } from "antd"; import React, { useEffect, useState } from "react"; -import NewBadge from "./common_components/NewBadge"; -import { useBaseUrl } from "./constants"; -import NotificationsManager from "./molecules/notifications_manager"; -import { addAllowedIP, deleteAllowedIP, getAllowedIPs, getSSOSettings } from "./networking"; -import SCIMConfig from "./SCIM"; -import LoggingSettings from "./Settings/AdminSettings/LoggingSettings/LoggingSettings"; -import SSOSettings from "./Settings/AdminSettings/SSOSettings/SSOSettings"; -import UISettings from "./Settings/AdminSettings/UISettings/UISettings"; -import HashicorpVault from "./Settings/AdminSettings/HashicorpVault/HashicorpVault"; -import PluginSettings from "./Settings/AdminSettings/PluginSettings/PluginSettings"; -import SSOModals from "./SSOModals"; -import UIAccessControlForm from "./UIAccessControlForm"; +import NewBadge from "@/components/common_components/NewBadge"; +import { useBaseUrl } from "@/components/constants"; +import NotificationsManager from "@/components/molecules/notifications_manager"; +import { addAllowedIP, deleteAllowedIP, getAllowedIPs, getSSOSettings } from "@/components/networking"; +import SCIMConfig from "@/components/SCIM"; +import LoggingSettings from "@/components/Settings/AdminSettings/LoggingSettings/LoggingSettings"; +import SSOSettings from "@/components/Settings/AdminSettings/SSOSettings/SSOSettings"; +import UISettings from "@/components/Settings/AdminSettings/UISettings/UISettings"; +import HashicorpVault from "@/components/Settings/AdminSettings/HashicorpVault/HashicorpVault"; +import PluginSettings from "@/components/Settings/AdminSettings/PluginSettings/PluginSettings"; +import SSOModals from "@/components/SSOModals"; +import UIAccessControlForm from "@/components/UIAccessControlForm"; const { Title, Paragraph, Text } = Typography; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx index aac835b02fc..47076acc9f0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx @@ -1,6 +1,6 @@ "use client"; -import AdminPanel from "@/components/AdminPanel"; +import AdminPanel from "./_components/AdminPanel"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx similarity index 97% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx index a73973bd742..66fa0dfa63f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx @@ -2,7 +2,7 @@ import { render } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import APIReferenceView from "./APIReferenceView"; -vi.mock("./components/CodeBlock", () => ({ +vi.mock("@/components/CodeBlock", () => ({ __esModule: true, default: ({ code }: { code: string }) =>
{code}
, })); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx similarity index 97% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx index 5861cc87e5b..333bd1cad13 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx @@ -1,8 +1,8 @@ "use client"; import React from "react"; import { Text, Tab, TabGroup, TabList, TabPanel, TabPanels, Grid } from "@tremor/react"; -import CodeBlock from "./components/CodeBlock"; -import DocLink from "@/app/(dashboard)/api-reference/components/DocLink"; +import CodeBlock from "@/components/CodeBlock"; +import DocLink from "./DocLink"; interface ApiRefProps { proxySettings: { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/DocLink.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/DocLink.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/DocLink.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/DocLink.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx index 42cf094f0bb..d7c977b0870 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx @@ -1,6 +1,6 @@ "use client"; -import APIReferenceView from "@/app/(dashboard)/api-reference/APIReferenceView"; +import APIReferenceView from "./_components/APIReferenceView"; import { DeprecationBanner } from "@/components/DeprecationBanner"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_modal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_modal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/constants.ts b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/constants.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/constants.ts rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/constants.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/edit_budget_modal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/edit_budget_modal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx index 547699411e7..ca34589a679 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx @@ -1,6 +1,6 @@ "use client"; -import BudgetPanel from "./components/budget_panel"; +import BudgetPanel from "./_components/budget_panel"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function Budgets() { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_dashboard.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_dashboard.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_health.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_health.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/CacheFieldSection.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFieldSection.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/CacheFieldSection.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFieldSection.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/CacheFormField.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFormField.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/CacheFormField.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFormField.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/cacheSettingsFields.ts b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsFields.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/cacheSettingsFields.ts rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsFields.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/cacheSettingsUtils.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.test.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/cacheSettingsUtils.test.ts rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.test.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/cacheSettingsUtils.ts b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/cacheSettingsUtils.ts rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/index.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/index.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/index.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/index.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/response_time_indicator.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/response_time_indicator.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/response_time_indicator.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/response_time_indicator.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/page.tsx index 0ef88ec9eb5..33f3e81c689 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/page.tsx @@ -1,6 +1,6 @@ "use client"; -import CacheDashboard from "./components/cache_dashboard"; +import CacheDashboard from "./_components/cache_dashboard"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function Caching() { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.test.tsx index 711a8795f15..a574f4b628e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.test.tsx @@ -5,7 +5,7 @@ import userEvent from "@testing-library/user-event"; import { renderWithProviders } from "../../../../../tests/test-utils"; import HowItWorks from "./how_it_works"; -vi.mock("@/app/(dashboard)/api-reference/components/CodeBlock", () => ({ +vi.mock("@/components/CodeBlock", () => ({ default: ({ code }: { code: string }) =>
{code}
, })); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.tsx index 79abf6baa31..5fa27551d16 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/components/how_it_works.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo } from "react"; import { Text, TextInput } from "@tremor/react"; -import CodeBlock from "@/app/(dashboard)/api-reference/components/CodeBlock"; +import CodeBlock from "@/components/CodeBlock"; const HowItWorks: React.FC = () => { const [responseCost, setResponseCost] = useState(""); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/EvaluationSettingsModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/EvaluationSettingsModal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/EvaluationSettingsModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/EvaluationSettingsModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailConfig.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailConfig.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailConfig.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailConfig.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailConfig.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailConfig.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailConfig.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailConfig.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailDetail.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailDetail.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailsOverview.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/GuardrailsOverview.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/ScoreChart.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/components/ScoreChart.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/page.tsx index 388ed168f17..0c4e69c2d80 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/page.tsx @@ -1,6 +1,6 @@ "use client"; -import GuardrailsMonitorView from "./components/GuardrailsMonitorView"; +import GuardrailsMonitorView from "./_components/GuardrailsMonitorView"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function GuardrailsMonitor() { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryEditModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryEditModal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryEditModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryEditModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryView.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/memory/components/MemoryView.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryView.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/memory/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/memory/page.tsx index 031a027d518..b88996c5396 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/memory/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/memory/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { MemoryView } from "./components/MemoryView"; +import { MemoryView } from "./_components/MemoryView"; import { DeprecationBanner } from "@/components/DeprecationBanner"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; diff --git a/ui/litellm-dashboard/src/components/usage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/old-usage/_components/usage.tsx similarity index 99% rename from ui/litellm-dashboard/src/components/usage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/old-usage/_components/usage.tsx index 91c12fd1fa2..01f8cb1cd45 100644 --- a/ui/litellm-dashboard/src/components/usage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/old-usage/_components/usage.tsx @@ -14,9 +14,9 @@ import { import React, { useState, useEffect } from "react"; -import ViewUserSpend from "./view_user_spend"; -import { ProxySettings } from "./user_dashboard"; -import UsageDatePicker from "./shared/usage_date_picker"; +import ViewUserSpend from "@/components/view_user_spend"; +import { ProxySettings } from "@/components/user_dashboard"; +import UsageDatePicker from "@/components/shared/usage_date_picker"; import { Grid, Col, @@ -48,8 +48,8 @@ import { adminGlobalActivity, adminGlobalActivityPerModel, getProxyUISettings, -} from "./networking"; -import TopKeyView from "./UsagePage/components/EntityUsage/TopKeyView"; +} from "@/components/networking"; +import TopKeyView from "@/components/UsagePage/components/EntityUsage/TopKeyView"; import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/old-usage/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/old-usage/page.tsx index cc1f2c35e44..138dd97e5e8 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/old-usage/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/old-usage/page.tsx @@ -1,6 +1,6 @@ "use client"; -import Usage from "@/components/usage"; +import Usage from "./_components/usage"; import { DeprecationBanner } from "@/components/DeprecationBanner"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; diff --git a/ui/litellm-dashboard/src/components/organizations.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/organizations/_components/organizations.test.tsx similarity index 87% rename from ui/litellm-dashboard/src/components/organizations.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/organizations/_components/organizations.test.tsx index 9be31be6170..75a6d30ac2e 100644 --- a/ui/litellm-dashboard/src/components/organizations.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/organizations/_components/organizations.test.tsx @@ -3,11 +3,11 @@ import { render } from "@testing-library/react"; import React from "react"; import { describe, expect, it, vi } from "vitest"; -vi.mock("./vector_store_management/VectorStoreSelector", () => ({ +vi.mock("@/components/vector_store_management/VectorStoreSelector", () => ({ __esModule: true, default: () => null, })); -vi.mock("./mcp_server_management/MCPServerSelector", () => ({ +vi.mock("@/components/mcp_server_management/MCPServerSelector", () => ({ __esModule: true, default: () => null, })); diff --git a/ui/litellm-dashboard/src/components/organizations.tsx b/ui/litellm-dashboard/src/app/(dashboard)/organizations/_components/organizations.tsx similarity index 96% rename from ui/litellm-dashboard/src/components/organizations.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/organizations/_components/organizations.tsx index edebc17087a..d3af5b62668 100644 --- a/ui/litellm-dashboard/src/components/organizations.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/organizations/_components/organizations.tsx @@ -28,16 +28,21 @@ import { Form, Input, Modal, Select as Select2, Tooltip } from "antd"; import { useQueryClient } from "@tanstack/react-query"; import React, { useState } from "react"; import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; -import DeleteResourceModal from "./common_components/DeleteResourceModal"; -import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; -import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_team_key"; -import MCPServerSelector from "./mcp_server_management/MCPServerSelector"; -import { ModelSelect } from "./ModelSelect/ModelSelect"; -import NotificationsManager from "./molecules/notifications_manager"; -import { Organization, organizationCreateCall, organizationDeleteCall, organizationListCall } from "./networking"; -import OrganizationInfoView from "./organization/organization_view"; -import NumericalInput from "./shared/numerical_input"; -import VectorStoreSelector from "./vector_store_management/VectorStoreSelector"; +import DeleteResourceModal from "@/components/common_components/DeleteResourceModal"; +import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; +import { getModelDisplayName } from "@/components/key_team_helpers/fetch_available_models_team_key"; +import MCPServerSelector from "@/components/mcp_server_management/MCPServerSelector"; +import { ModelSelect } from "@/components/ModelSelect/ModelSelect"; +import NotificationsManager from "@/components/molecules/notifications_manager"; +import { + Organization, + organizationCreateCall, + organizationDeleteCall, + organizationListCall, +} from "@/components/networking"; +import OrganizationInfoView from "@/components/organization/organization_view"; +import NumericalInput from "@/components/shared/numerical_input"; +import VectorStoreSelector from "@/components/vector_store_management/VectorStoreSelector"; interface OrganizationsTableProps { userRole: string; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/organizations/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/organizations/page.tsx index 87e0faf9cce..649e54f63eb 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/organizations/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/organizations/page.tsx @@ -1,6 +1,6 @@ "use client"; -import OrganizationsTable from "@/components/organizations"; +import OrganizationsTable from "./_components/organizations"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function OrganizationsPage() { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx index 35f5dcf06c0..d4333b95c62 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx @@ -11,7 +11,7 @@ import { } from "@ant-design/icons"; import { Button, Input, Modal, Select, Spin, Tabs } from "antd"; import React, { useCallback, useEffect, useState } from "react"; -import CodeBlock from "@/app/(dashboard)/api-reference/components/CodeBlock"; +import CodeBlock from "@/components/CodeBlock"; import NotificationsManager from "@/components/molecules/notifications_manager"; import { keyCreateCall, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectDetailsPage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectDetailsPage.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectDetailsPage.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectDetailsPage.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectDetailsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectDetailsPage.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectDetailsPage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectDetailsPage.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysSection.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysSection.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysSection.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysSection.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysSection.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysSection.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysSection.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysSection.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysTable.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysTable.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysTable.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectKeysTable.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectKeysTable.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/CreateProjectModal.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/CreateProjectModal.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/CreateProjectModal.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/CreateProjectModal.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/CreateProjectModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/CreateProjectModal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/CreateProjectModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/CreateProjectModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/EditProjectModal.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/EditProjectModal.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/EditProjectModal.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/EditProjectModal.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/EditProjectModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/EditProjectModal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/EditProjectModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/EditProjectModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/ProjectBaseForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/ProjectBaseForm.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/ProjectBaseForm.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/ProjectBaseForm.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/ProjectBaseForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/ProjectBaseForm.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/ProjectBaseForm.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/ProjectBaseForm.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/projectFormUtils.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormUtils.test.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/projectFormUtils.test.ts rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormUtils.test.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/projectFormUtils.ts b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormUtils.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectModals/projectFormUtils.ts rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormUtils.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/projects/components/ProjectsPage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/page.tsx index 62b67118109..2ba014592c9 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/projects/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/projects/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { ProjectsPage } from "./components/ProjectsPage"; +import { ProjectsPage } from "./_components/ProjectsPage"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function Projects() { diff --git a/ui/litellm-dashboard/src/components/general_settings.tsx b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx similarity index 96% rename from ui/litellm-dashboard/src/components/general_settings.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx index 038547c6e0e..3955e80f5e9 100644 --- a/ui/litellm-dashboard/src/components/general_settings.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx @@ -13,14 +13,14 @@ import { Switch, } from "@tremor/react"; import { TabPanel, TabPanels, TabGroup, TabList, Tab } from "@tremor/react"; -import { getGeneralSettingsCall, updateConfigFieldSetting, deleteConfigFieldSetting } from "./networking"; +import { getGeneralSettingsCall, updateConfigFieldSetting, deleteConfigFieldSetting } from "@/components/networking"; import { InputNumber } from "antd"; import { TrashIcon } from "@heroicons/react/outline"; import { StatusBadge } from "@/components/shared/table_cells"; -import RouterSettings from "./router_settings"; -import Fallbacks from "./Settings/RouterSettings/Fallbacks/Fallbacks"; -import RoutingGroups from "./routing_groups"; +import RouterSettings from "@/components/router_settings"; +import Fallbacks from "@/components/Settings/RouterSettings/Fallbacks/Fallbacks"; +import RoutingGroups from "@/components/routing_groups"; interface GeneralSettingsPageProps { accessToken: string | null; userRole: string | null; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/page.tsx index 46029b529ec..90f41ac58a4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/page.tsx @@ -1,6 +1,6 @@ "use client"; -import GeneralSettings from "@/components/general_settings"; +import GeneralSettings from "./_components/general_settings"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function RouterSettingsPage() { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/CodeBlock.tsx b/ui/litellm-dashboard/src/components/CodeBlock.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/CodeBlock.tsx rename to ui/litellm-dashboard/src/components/CodeBlock.tsx diff --git a/ui/litellm-dashboard/tests/CreateKeyPage.expiredToken.test.tsx b/ui/litellm-dashboard/tests/CreateKeyPage.expiredToken.test.tsx index 660c49fff77..07ed1cb5c2e 100644 --- a/ui/litellm-dashboard/tests/CreateKeyPage.expiredToken.test.tsx +++ b/ui/litellm-dashboard/tests/CreateKeyPage.expiredToken.test.tsx @@ -108,13 +108,15 @@ vi.mock("@/components/navbar", () => ({ default: stub("navbar") })); vi.mock("@/components/user_dashboard", () => ({ default: stub("user-dashboard") })); vi.mock("@/components/templates/model_dashboard", () => ({ default: stub("model-dashboard") })); vi.mock("@/components/teams", () => ({ default: stub("teams") })); -vi.mock("@/components/organizations", () => ({ +vi.mock("@/app/(dashboard)/organizations/_components/organizations", () => ({ default: stub("organizations"), fetchOrganizations: vi.fn(), // consumed in effects })); vi.mock("@/components/admins", () => ({ default: stub("admin-panel") })); vi.mock("@/components/settings", () => ({ default: stub("settings") })); -vi.mock("@/components/general_settings", () => ({ default: stub("general-settings") })); +vi.mock("@/app/(dashboard)/router-settings/_components/general_settings", () => ({ + default: stub("general-settings"), +})); vi.mock("@/components/pass_through_settings", () => ({ default: stub("pass-through-settings") })); vi.mock("@/components/budgets/budget_panel", () => ({ default: stub("budget-panel") })); vi.mock("@/components/view_logs", () => ({ default: stub("spend-logs") })); @@ -123,7 +125,7 @@ vi.mock("@/components/new_usage", () => ({ default: stub("new-usage") })); vi.mock("@/components/api_ref", () => ({ default: stub("api-ref") })); vi.mock("@/components/chat_ui/ChatUI", () => ({ default: stub("chat-ui") })); vi.mock("@/components/leftnav", () => ({ default: stub("sidebar") })); -vi.mock("@/components/usage", () => ({ default: stub("usage") })); +vi.mock("@/app/(dashboard)/old-usage/_components/usage", () => ({ default: stub("usage") })); vi.mock("@/components/cache_dashboard", () => ({ default: stub("cache-dashboard") })); vi.mock("@/components/guardrails", () => ({ default: stub("guardrails") })); vi.mock("@/components/prompts", () => ({ default: stub("prompts") })); From 3e1383f529b796fc557e7886f76c66c63f2427c7 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 9 Jul 2026 17:45:05 -0700 Subject: [PATCH 096/399] fix(ui): reset page and sort correctly in Team virtual keys table Addresses three issues in the migrated Team Info virtual keys table, all pre-existing behavior carried over from the tremor version: - Changing the sort now resets to page 1. Previously handleSortingChange routed through handleFilterChange with skipDebounce=true, which skipped the pageIndex reset, so sorting while on a later page asked the server for that page of the newly sorted results (an arbitrary slice). - Reset Filters now restores the default sort. It previously reset the filter fields and page but never touched the sorting state that actually drives the query, so the sort indicator and server order persisted. - Removes the dead Sort By / Sort Order keys from the filters object; sort is derived solely from the sorting state, so those keys were written but never read. Sort now lives in one place. Adds regression tests for the page-reset-on-sort and sort-reset-on-filter-reset behaviors (both fail if either fix is reverted). --- ui/litellm-dashboard/eslint-metrics.json | 2 +- .../team/TeamVirtualKeysTable.test.tsx | 53 +++++++++++++++++++ .../components/team/TeamVirtualKeysTable.tsx | 38 ++++--------- 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index 036af5bc789..0900584f5ce 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,7 +1,7 @@ { "@typescript-eslint/no-explicit-any": 1980, "complexity": 128, - "local/no-large-inline-object-arg": 511, + "local/no-large-inline-object-arg": 509, "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx index 9eb0282580b..fd81aaaad99 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx @@ -236,6 +236,59 @@ describe("TeamVirtualKeysTable", () => { ); }); + it("resets to the first page when the sort changes", async () => { + const user = userEvent.setup(); + mockUseKeys.mockImplementation( + (page: number) => + ({ + data: { + keys: [createMockKey({ token: `sk-p${page}`, key_alias: `page${page}_key` })], + total_count: 100, + current_page: page, + total_pages: 2, + }, + isPending: false, + isFetching: false, + refetch: vi.fn(), + }) as unknown as ReturnType, + ); + + renderWithProviders(); + + await user.click(await screen.findByTestId("pagination-next")); + await waitFor(() => expect(mockUseKeys).toHaveBeenLastCalledWith(2, 50, expect.anything())); + + await user.click(screen.getByTestId("sort-header-created_at")); + await waitFor(() => expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.anything())); + }); + + it("resets the sort order to the default when filters are reset", async () => { + const user = userEvent.setup(); + const result = { + data: { keys: [createMockKey()], total_count: 1, current_page: 1, total_pages: 1 }, + isPending: false, + isFetching: false, + refetch: vi.fn(), + } as unknown as ReturnType; + mockUseKeys.mockReturnValue(result); + + renderWithProviders(); + + await user.click(await screen.findByTestId("sort-header-created_at")); + await waitFor(() => + expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ sortOrder: "asc" })), + ); + + await user.click(screen.getByRole("button", { name: "Reset Filters" })); + await waitFor(() => + expect(mockUseKeys).toHaveBeenLastCalledWith( + 1, + 50, + expect.objectContaining({ sortBy: "created_at", sortOrder: "desc" }), + ), + ); + }); + it("should show Loading keys when isPending", async () => { mockUseKeys.mockReturnValue({ data: undefined, diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index 778b82a8fc5..73f524e13f8 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -27,10 +27,12 @@ interface TeamVirtualKeysTableProps { * TeamVirtualKeysTable – variant of VirtualKeysTable scoped to a single team. * Displays all virtual keys belonging to the team with same format and styling. */ +const DEFAULT_SORTING: SortingState = [{ id: "created_at", desc: true }]; + export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVirtualKeysTableProps) { const { accessToken } = useAuthorized(); const [selectedKey, setSelectedKey] = useState(null); - const [sorting, setSorting] = useState([{ id: "created_at", desc: true }]); + const [sorting, setSorting] = useState(DEFAULT_SORTING); const [tablePagination, setTablePagination] = useState({ pageIndex: 0, pageSize: 50, @@ -39,8 +41,6 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi "Organization ID": "", "Key Alias": "", "User ID": "", - "Sort By": "created_at", - "Sort Order": "desc", }); const sortBy = sorting.length > 0 ? sorting[0].id : "created_at"; @@ -116,18 +116,14 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi return () => window.removeEventListener("storage", handleStorageChange); }, [handleStorageChange]); - const handleFilterChange = useCallback((newFilters: Record, skipDebounce = false) => { + const handleFilterChange = useCallback((newFilters: Record) => { setFilters((prev) => ({ ...prev, "Organization ID": newFilters["Organization ID"] ?? prev["Organization ID"], "Key Alias": newFilters["Key Alias"] ?? prev["Key Alias"], "User ID": newFilters["User ID"] ?? prev["User ID"], - "Sort By": newFilters["Sort By"] ?? prev["Sort By"] ?? "created_at", - "Sort Order": newFilters["Sort Order"] ?? prev["Sort Order"] ?? "desc", })); - if (!skipDebounce) { - setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); - } + setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); }, []); const handleFilterReset = useCallback(() => { @@ -135,9 +131,8 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi "Organization ID": "", "Key Alias": "", "User ID": "", - "Sort By": "created_at", - "Sort Order": "desc", }); + setSorting(DEFAULT_SORTING); setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); }, []); @@ -492,23 +487,10 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi [expandedAccordions], ); - const handleSortingChange = useCallback( - (updaterOrValue: React.SetStateAction) => { - const newSorting = typeof updaterOrValue === "function" ? updaterOrValue(sorting) : updaterOrValue; - setSorting(newSorting); - if (newSorting?.length > 0) { - const sortState = newSorting[0]; - handleFilterChange( - { - "Sort By": sortState.id, - "Sort Order": sortState.desc ? "desc" : "asc", - }, - true, - ); - } - }, - [sorting, handleFilterChange], - ); + const handleSortingChange = useCallback((updaterOrValue: React.SetStateAction) => { + setSorting(updaterOrValue); + setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); + }, []); return (
From 592510ec18b880fc5bea533af18afa317dd1e67d Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Thu, 9 Jul 2026 18:18:52 -0700 Subject: [PATCH 097/399] feat(ui): shadcn charts foundation with tremor-compatible wrappers (#32668) --- ui/litellm-dashboard/eslint-metrics.json | 4 +- ui/litellm-dashboard/eslint-suppressions.json | 74 ++-- ui/litellm-dashboard/package-lock.json | 221 ++++++++++-- ui/litellm-dashboard/package.json | 1 + .../_components/ScoreChart.test.tsx | 38 +- .../_components/ScoreChart.tsx | 50 +-- .../shared/charts/area_chart.test.tsx | 36 ++ .../components/shared/charts/area_chart.tsx | 92 +++++ .../shared/charts/bar_chart.test.tsx | 119 +++++++ .../components/shared/charts/bar_chart.tsx | 119 +++++++ .../shared/charts/chart_legend.test.tsx | 32 ++ .../components/shared/charts/chart_legend.tsx | 25 ++ .../shared/charts/chart_tooltip.test.tsx | 101 ++++++ .../shared/charts/chart_tooltip.tsx | 97 ++++++ .../src/components/shared/charts/colors.ts | 58 ++++ .../shared/charts/donut_chart.test.tsx | 38 ++ .../components/shared/charts/donut_chart.tsx | 67 ++++ .../src/components/shared/charts/index.ts | 12 + .../src/components/ui/card.tsx | 86 +++++ .../src/components/ui/chart.test.tsx | 38 ++ .../src/components/ui/chart.tsx | 324 ++++++++++++++++++ .../src/components/ui/ref-forwarding.test.tsx | 42 +++ ui/litellm-dashboard/tests/setupTests.ts | 37 +- 23 files changed, 1582 insertions(+), 129 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/area_chart.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/bar_chart.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/chart_legend.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/chart_legend.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/colors.ts create mode 100644 ui/litellm-dashboard/src/components/shared/charts/donut_chart.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/donut_chart.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/charts/index.ts create mode 100644 ui/litellm-dashboard/src/components/ui/card.tsx create mode 100644 ui/litellm-dashboard/src/components/ui/chart.test.tsx create mode 100644 ui/litellm-dashboard/src/components/ui/chart.tsx diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index d69b3e1f729..2e204c63a48 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,6 +1,6 @@ { - "@typescript-eslint/no-explicit-any": 1980, - "complexity": 128, + "@typescript-eslint/no-explicit-any": 1978, + "complexity": 129, "local/no-large-inline-object-arg": 512, "local/no-long-condition-chain": 233, "max-depth": 59, diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index b490cf71768..32ab92cbcc9 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -4,6 +4,14 @@ "count": 1 } }, + "src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, "src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx": { "no-restricted-imports": { "count": 1 @@ -156,16 +164,6 @@ "count": 8 } }, - "src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/hooks/accessGroups/useAccessGroupDetails.ts": { "no-restricted-syntax": { "count": 1 @@ -373,6 +371,22 @@ "count": 1 } }, + "src/app/(dashboard)/old-usage/_components/usage.tsx": { + "no-restricted-imports": { + "count": 2 + }, + "react-hooks/immutability": { + "count": 1 + }, + "react-hooks/purity": { + "count": 1 + } + }, + "src/app/(dashboard)/organizations/_components/organizations.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, "src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx": { "no-restricted-imports": { "count": 1 @@ -649,6 +663,14 @@ "count": 1 } }, + "src/app/(dashboard)/router-settings/_components/general_settings.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "no-restricted-imports": { + "count": 2 + } + }, "src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx": { "no-restricted-imports": { "count": 1 @@ -851,14 +873,6 @@ "count": 1 } }, - "src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/components/CreateUserButton.tsx": { "no-restricted-imports": { "count": 1 @@ -1520,14 +1534,6 @@ "count": 1 } }, - "src/app/(dashboard)/router-settings/_components/general_settings.tsx": { - "no-nested-ternary": { - "count": 3 - }, - "no-restricted-imports": { - "count": 2 - } - }, "src/components/guardrails.tsx": { "react-hooks/set-state-in-effect": { "count": 1 @@ -2028,11 +2034,6 @@ "count": 1 } }, - "src/app/(dashboard)/organizations/_components/organizations.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/page_utils.test.ts": { "max-nested-callbacks": { "count": 3 @@ -2371,17 +2372,6 @@ "count": 1 } }, - "src/app/(dashboard)/old-usage/_components/usage.tsx": { - "no-restricted-imports": { - "count": 2 - }, - "react-hooks/immutability": { - "count": 1 - }, - "react-hooks/purity": { - "count": 1 - } - }, "src/components/user_agent_activity.tsx": { "no-restricted-imports": { "count": 2 diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index ae3660f59e9..56c0a4f9500 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -34,6 +34,7 @@ "react-json-view-lite": "2.5.0", "react-markdown": "9.1.0", "react-syntax-highlighter": "15.6.6", + "recharts": "3.9.2", "remark-gfm": "4.0.1", "tailwind-merge": "3.4.0", "uuid": "14.0.0" @@ -2927,6 +2928,32 @@ "npm": ">=9.5.0" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", + "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.61.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", @@ -3284,6 +3311,18 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -3804,6 +3843,42 @@ "react-dom": ">=16.6.0" } }, + "node_modules/@tremor/react/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/@tremor/react/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/@tremor/react/node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@tremor/react/node_modules/tailwind-merge": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", @@ -3814,6 +3889,28 @@ "url": "https://github.com/sponsors/dcastil" } }, + "node_modules/@tremor/react/node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", @@ -4069,6 +4166,12 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.60.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", @@ -6309,6 +6412,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.49.0.tgz", + "integrity": "sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -6872,9 +6985,9 @@ } }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, "node_modules/expect-type": { @@ -6901,9 +7014,9 @@ "license": "MIT" }, "node_modules/fast-equals": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", - "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.1.tgz", + "integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==", "license": "MIT", "engines": { "node": ">=6.0.0" @@ -7688,6 +7801,16 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.11.tgz", + "integrity": "sha512-qzXuyXAkPySAGYkfsAwodDPWT8Zm7/Uo5BNt4BjhMhG5WlWyZZ4wQqnWwdS8kjlQ1Cwu6gjw3A6+0gTQwlyYtw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -11589,7 +11712,6 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, "license": "MIT" }, "node_modules/react-json-view-lite": { @@ -11631,6 +11753,29 @@ "react": ">=18" } }, + "node_modules/react-redux": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", + "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -11707,26 +11852,33 @@ } }, "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.9.2.tgz", + "integrity": "sha512-G4fy+Pk46RaXgwWMh+Nzhyo/lbFAVqXo9gtetlyehe6Ehge9CsgDuOTwQDD+i1+llaLktNBiNq4bhnGlDRXFtw==", "license": "MIT", + "workspaces": [ + "www" + ], "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/recharts-scale": { @@ -11738,12 +11890,6 @@ "decimal.js-light": "^2.4.1" } }, - "node_modules/recharts/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -11758,6 +11904,21 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -13429,9 +13590,9 @@ } }, "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", "license": "MIT AND ISC", "dependencies": { "@types/d3-array": "^3.0.3", diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index 1b0ce315e4d..1747a40da56 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -50,6 +50,7 @@ "react-json-view-lite": "2.5.0", "react-markdown": "9.1.0", "react-syntax-highlighter": "15.6.6", + "recharts": "3.9.2", "remark-gfm": "4.0.1", "tailwind-merge": "3.4.0", "uuid": "14.0.0" diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx index 3a36eb9621e..dba34ea9a86 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.test.tsx @@ -1,35 +1,9 @@ import React from "react"; -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect } from "vitest"; import { screen } from "@testing-library/react"; import { renderWithProviders } from "../../../../../tests/test-utils"; import { ScoreChart } from "./ScoreChart"; -vi.mock("@tremor/react", async (importOriginal) => { - const actual = await importOriginal(); - // Re-apply the global Button/Tooltip overrides from tests/setupTests.ts. A file-level - // vi.mock fully replaces the setup-level mock, so without this the real Tremor Button - // leaks through and its useTooltip(300) schedules a native setTimeout that can fire - // post-teardown -> "window is not defined". - return { - ...actual, - BarChart: ({ data, categories }: { data: any[]; categories: string[] }) => ( -
- {data.map((d, i) => ( - - {d.date}: {categories.map((c) => `${c}=${d[c]}`).join(", ")} - - ))} -
- ), - Button: React.forwardRef(({ children, ...props }, ref) => ( - - )), - Tooltip: ({ children }: { children?: React.ReactNode }) => <>{children}, - }; -}); - describe("ScoreChart", () => { it("should render the title", () => { renderWithProviders(); @@ -55,10 +29,14 @@ describe("ScoreChart", () => { { date: "2026-03-02", passed: 15, blocked: 1 }, ]; - renderWithProviders(); + const { container } = renderWithProviders(); expect(screen.queryByText("No chart data for this period")).not.toBeInTheDocument(); - expect(screen.getByText(/2026-03-01/)).toBeInTheDocument(); - expect(screen.getByText(/2026-03-02/)).toBeInTheDocument(); + expect(screen.getByText("passed")).toBeInTheDocument(); + expect(screen.getByText("blocked")).toBeInTheDocument(); + expect(screen.getAllByText(/2026-03-01/).length).toBeGreaterThan(0); + expect(screen.getAllByText(/2026-03-02/).length).toBeGreaterThan(0); + const bars = container.querySelectorAll(".recharts-bar"); + expect(bars).toHaveLength(2); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx index daa6054a552..bc11a6fd3e0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/ScoreChart.tsx @@ -1,9 +1,10 @@ -import { BarChart, Card, Title } from "@tremor/react"; import React from "react"; +import { BarChart } from "@/components/shared/charts"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; /** * Overview chart: Request Outcomes Over Time (passed vs blocked). - * Uses Tremor BarChart with stacked data. Data from usage/overview API (chart array). + * Stacked bar chart. Data from usage/overview API (chart array). */ interface ScoreChartProps { data?: Array<{ date: string; passed: number; blocked: number }>; @@ -13,26 +14,31 @@ export function ScoreChart({ data }: ScoreChartProps) { const chartData = data && data.length > 0 ? data : []; return ( - - Request Outcomes Over Time -
- {chartData.length > 0 ? ( - v.toLocaleString()} - yAxisWidth={48} - showLegend={true} - stack={true} - /> - ) : ( -
- No chart data for this period -
- )} -
+ + + Request Outcomes Over Time + + +
+ {chartData.length > 0 ? ( + v.toLocaleString()} + yAxisWidth={48} + showLegend={true} + stack={true} + className="h-full" + /> + ) : ( +
+ No chart data for this period +
+ )} +
+
); } diff --git a/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx new file mode 100644 index 00000000000..cd033c5ce27 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx @@ -0,0 +1,36 @@ +import { render } from "@testing-library/react"; +import React from "react"; +import { describe, expect, it } from "vitest"; +import { AreaChart } from "./area_chart"; + +const data = [ + { date: "2026-03-01", tokens: 100, requests: 10 }, + { date: "2026-03-02", tokens: 150, requests: 12 }, +]; + +describe("AreaChart", () => { + it("renders one area per category with the mapped stroke colors", () => { + const { container } = render( + , + ); + + const curves = Array.from(container.querySelectorAll("path.recharts-area-curve")); + expect(curves).toHaveLength(2); + const strokes = new Set(curves.map((curve) => curve.getAttribute("stroke"))); + expect(strokes).toEqual(new Set(["var(--color-blue-500, #3b82f6)", "var(--color-cyan-500, #06b6d4)"])); + }); + + it("renders a fade-out gradient fill per category", () => { + const { container } = render( + , + ); + + const gradients = container.querySelectorAll("defs linearGradient"); + expect(gradients).toHaveLength(2); + const areas = Array.from(container.querySelectorAll("path.recharts-area-area")); + expect(areas).toHaveLength(2); + for (const area of areas) { + expect(area.getAttribute("fill")).toMatch(/^url\(#fill-/); + } + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/charts/area_chart.tsx b/ui/litellm-dashboard/src/components/shared/charts/area_chart.tsx new file mode 100644 index 00000000000..794baa13cf7 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/area_chart.tsx @@ -0,0 +1,92 @@ +"use client"; + +import * as React from "react"; +import { Area, AreaChart as RechartsAreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; +import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, type ChartConfig } from "@/components/ui/chart"; +import { cn } from "@/lib/cva.config"; +import { ValueTooltip, type ChartTooltipComponent } from "./chart_tooltip"; +import { categoryFills, type ChartColor } from "./colors"; + +export type AreaChartProps> = { + data: readonly TDatum[]; + index: string; + categories: readonly string[]; + colors?: readonly ChartColor[]; + valueFormatter?: (value: number) => string; + yAxisWidth?: number; + showLegend?: boolean; + showGridLines?: boolean; + showTooltip?: boolean; + customTooltip?: ChartTooltipComponent; + className?: string; + style?: React.CSSProperties; +}; + +export function AreaChart>({ + data, + index, + categories, + colors, + valueFormatter, + yAxisWidth = 56, + showLegend = true, + showGridLines = true, + showTooltip = true, + customTooltip, + className, + style, +}: AreaChartProps) { + const gradientId = React.useId().replace(/:/g, ""); + const fills = categoryFills(categories.length, colors); + const config: ChartConfig = Object.fromEntries(categories.map((category) => [category, { label: category }])); + const TooltipContent = customTooltip ?? ValueTooltip; + + return ( + + + + {categories.map((category, i) => ( + + + + + ))} + + {showGridLines && } + + + {showTooltip && ( + ( + + )} + /> + )} + {showLegend && ( + } + /> + )} + {categories.map((category, i) => ( + + ))} + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx new file mode 100644 index 00000000000..d5253c86c6f --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx @@ -0,0 +1,119 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import React from "react"; +import { describe, expect, it, vi } from "vitest"; +import { BarChart } from "./bar_chart"; + +const data = [ + { date: "2026-03-01", passed: 10, blocked: 2 }, + { date: "2026-03-02", passed: 15, blocked: 1 }, +]; + +describe("BarChart", () => { + it("renders one bar series per category with the mapped tremor colors", () => { + const { container } = render( + , + ); + + const rectangles = Array.from(container.querySelectorAll("path.recharts-rectangle")); + expect(rectangles).toHaveLength(4); + const fills = new Set(rectangles.map((rect) => rect.getAttribute("fill"))); + expect(fills).toEqual(new Set(["var(--color-green-500, #22c55e)", "var(--color-red-500, #ef4444)"])); + }); + + it("falls back to the tremor default color cycle when no colors are passed", () => { + const { container } = render(); + + const fills = new Set( + Array.from(container.querySelectorAll("path.recharts-rectangle")).map((rect) => rect.getAttribute("fill")), + ); + expect(fills).toEqual(new Set(["var(--color-blue-500, #3b82f6)", "var(--color-cyan-500, #06b6d4)"])); + }); + + it("fires onValueChange with the datum and clicked category", () => { + const onValueChange = vi.fn(); + const { container } = render( + , + ); + + const firstRect = container.querySelector("path.recharts-rectangle"); + expect(firstRect).not.toBeNull(); + fireEvent.click(firstRect!); + + expect(onValueChange).toHaveBeenCalledTimes(1); + const expectedClickItem = { + date: "2026-03-01", + passed: 10, + blocked: 2, + categoryClicked: "passed", + }; + expect(onValueChange).toHaveBeenCalledWith(expectedClickItem); + }); + + it("renders category labels on the y axis in vertical layout", () => { + render( + , + ); + + expect(screen.getAllByText("alpha").length).toBeGreaterThan(0); + expect(screen.getAllByText("beta").length).toBeGreaterThan(0); + }); + + it("applies valueFormatter to the value axis ticks", () => { + render( + `${v} req`} + />, + ); + + expect(screen.getAllByText(/ req$/).length).toBeGreaterThan(0); + }); + + it("renders a legend by default, matching tremor, and hides it when showLegend is false", () => { + const { container, rerender } = render( + , + ); + expect(screen.getByText("passed")).toBeInTheDocument(); + expect(container.querySelector(".recharts-legend-wrapper")).not.toBeNull(); + + rerender(); + expect(screen.queryByText("passed")).not.toBeInTheDocument(); + }); + + it("emits no per-chart style tag; colors flow through fills, not CSS vars", () => { + const { container } = render( + , + ); + expect(container.querySelector("style")).toBeNull(); + }); + + it("stacks bars into a single column per index when stack is set", () => { + const { container } = render( + , + ); + + const xPositions = Array.from(container.querySelectorAll("path.recharts-rectangle")).map( + (rect) => rect.getAttribute("d")?.split(",")[0], + ); + expect(new Set(xPositions).size).toBe(2); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/charts/bar_chart.tsx b/ui/litellm-dashboard/src/components/shared/charts/bar_chart.tsx new file mode 100644 index 00000000000..6ee3319dc10 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/bar_chart.tsx @@ -0,0 +1,119 @@ +"use client"; + +import * as React from "react"; +import { Bar, BarChart as RechartsBarChart, CartesianGrid, XAxis, YAxis } from "recharts"; +import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, type ChartConfig } from "@/components/ui/chart"; +import { cn } from "@/lib/cva.config"; +import { ValueTooltip, type ChartTooltipComponent } from "./chart_tooltip"; +import { categoryFills, type ChartColor } from "./colors"; + +export type BarChartProps> = { + data: readonly TDatum[]; + index: string; + categories: readonly string[]; + colors?: readonly ChartColor[]; + valueFormatter?: (value: number) => string; + stack?: boolean; + layout?: "horizontal" | "vertical"; + yAxisWidth?: number; + tickGap?: number; + showLegend?: boolean; + showXAxis?: boolean; + showGridLines?: boolean; + showTooltip?: boolean; + customTooltip?: ChartTooltipComponent; + onValueChange?: (item: TDatum & { categoryClicked: string }) => void; + className?: string; + style?: React.CSSProperties; +}; + +export function BarChart>({ + data, + index, + categories, + colors, + valueFormatter, + stack = false, + layout = "horizontal", + yAxisWidth = 56, + tickGap = 5, + showLegend = true, + showXAxis = true, + showGridLines = true, + showTooltip = true, + customTooltip, + onValueChange, + className, + style, +}: BarChartProps) { + const fills = categoryFills(categories.length, colors); + const config: ChartConfig = Object.fromEntries(categories.map((category) => [category, { label: category }])); + const vertical = layout === "vertical"; + const TooltipContent = customTooltip ?? ValueTooltip; + + return ( + + + {showGridLines && } + {vertical ? ( + + ) : ( + + )} + {vertical ? ( + + ) : ( + + )} + {showTooltip && ( + ( + + )} + /> + )} + {showLegend && ( + } + /> + )} + {categories.map((category, i) => ( + { + if (item.payload) onValueChange({ ...item.payload, categoryClicked: category }); + } + : undefined + } + /> + ))} + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/charts/chart_legend.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/chart_legend.test.tsx new file mode 100644 index 00000000000..889927aca43 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/chart_legend.test.tsx @@ -0,0 +1,32 @@ +import { render, screen } from "@testing-library/react"; +import React from "react"; +import { describe, expect, it } from "vitest"; +import { CustomLegend } from "./chart_legend"; + +describe("CustomLegend", () => { + it("renders title-cased category names without the metrics prefix", () => { + render(); + + expect(screen.getByText("Total Tokens")).toBeInTheDocument(); + expect(screen.getByText("Spend")).toBeInTheDocument(); + }); + + it("matches colors to categories by index with theme-var values", () => { + const { container } = render( + , + ); + + const dots = Array.from(container.querySelectorAll('span[style*="background-color"]')); + expect(dots[0]?.getAttribute("style")).toContain("--color-blue-500"); + expect(dots[1]?.getAttribute("style")).toContain("--color-green-500"); + }); + + it("cycles colors when there are more categories than colors", () => { + const { container } = render( + , + ); + + const dots = Array.from(container.querySelectorAll('span[style*="background-color"]')); + expect(dots[2]?.getAttribute("style")).toContain("--color-blue-500"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/charts/chart_legend.tsx b/ui/litellm-dashboard/src/components/shared/charts/chart_legend.tsx new file mode 100644 index 00000000000..da252d8bf63 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/chart_legend.tsx @@ -0,0 +1,25 @@ +"use client"; + +import * as React from "react"; +import { formatCategoryName } from "./chart_tooltip"; +import { chartColorValue, type ChartColor } from "./colors"; + +export const CustomLegend = ({ + categories, + colors, +}: { + categories: readonly string[]; + colors: readonly ChartColor[]; +}) => ( +
+ {categories.map((category, idx) => ( +
+ +

{formatCategoryName(category)}

+
+ ))} +
+); diff --git a/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx new file mode 100644 index 00000000000..7afc7532760 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx @@ -0,0 +1,101 @@ +import { render, screen } from "@testing-library/react"; +import React from "react"; +import { describe, expect, it } from "vitest"; +import { CustomTooltip, ValueTooltip, type ChartTooltipProps } from "./chart_tooltip"; + +const metricsPayload = ( + dataKey: string, + value: number, + color = "#3b82f6", +): NonNullable[number] => + ({ + dataKey, + value, + color, + payload: { + date: "2026-01-15", + metrics: { + total_tokens: 1000, + prompt_tokens: 600, + completion_tokens: 400, + spend: 1234.567, + api_requests: 10, + }, + }, + }) as NonNullable[number]; + +describe("CustomTooltip", () => { + it("returns null when not active or payload is empty", () => { + const inactive = render( + , + ); + expect(inactive.container.firstChild).toBeNull(); + + const empty = render(); + expect(empty.container.firstChild).toBeNull(); + }); + + it("renders the label and title-cased category names without the metrics prefix", () => { + render(); + + expect(screen.getByText("2026-01-15")).toBeInTheDocument(); + expect(screen.getByText("Total Tokens")).toBeInTheDocument(); + expect(screen.getByText("1,000")).toBeInTheDocument(); + }); + + it("formats spend values as dollars with two decimals", () => { + render(); + + expect(screen.getByText("$1,234.57")).toBeInTheDocument(); + }); + + it("shows N/A for metrics missing from the row payload", () => { + render(); + + expect(screen.getByText("N/A")).toBeInTheDocument(); + }); + + it("uses the series color for the indicator dot", () => { + const { container } = render( + , + ); + + const dot = container.querySelector('span[style*="background-color"]'); + expect(dot?.getAttribute("style")).toContain("--color-blue-500"); + }); +}); + +describe("ValueTooltip", () => { + const payload = [ + { + dataKey: "passed", + name: "passed", + value: 1000, + color: "#22c55e", + payload: { date: "2026-01-15", passed: 1000 }, + } as NonNullable[number], + ]; + + it("returns null when not active", () => { + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); + + it("renders label, series name, and locale-formatted value by default", () => { + render(); + + expect(screen.getByText("2026-01-15")).toBeInTheDocument(); + expect(screen.getByText("passed")).toBeInTheDocument(); + expect(screen.getByText("1,000")).toBeInTheDocument(); + }); + + it("applies the valueFormatter to values", () => { + render( `$${v}`} />); + + expect(screen.getByText("$1000")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.tsx b/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.tsx new file mode 100644 index 00000000000..2644b8f720c --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.tsx @@ -0,0 +1,97 @@ +"use client"; + +import * as React from "react"; +import type { TooltipContentProps, TooltipValueType } from "recharts"; + +export type ChartTooltipProps = Pick< + TooltipContentProps, + "active" | "payload" | "label" +>; + +export type ChartTooltipComponent = React.ComponentType; + +export const formatCategoryName = (name: string): string => + name + .replace("metrics.", "") + .replace(/_/g, " ") + .split(" ") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + +export const ValueTooltip = ({ + active, + payload, + label, + valueFormatter, +}: ChartTooltipProps & { valueFormatter?: (value: number) => string }) => { + if (!active || !payload || payload.length === 0) return null; + + const formatValue = (value: unknown): string => { + if (typeof value === "number") return valueFormatter ? valueFormatter(value) : value.toLocaleString(); + return value == null ? "" : String(value); + }; + + return ( +
+ {label != null &&

{String(label)}

} +
+ {payload.map((item, idx) => ( +
+
+ + {String(item.name ?? item.dataKey ?? "")} +
+ {formatValue(item.value)} +
+ ))} +
+
+ ); +}; + +const rawMetricValue = (row: unknown, dataKey: string): number | undefined => { + if (typeof row !== "object" || row === null || !("metrics" in row)) return undefined; + const metrics = (row as { metrics: unknown }).metrics; + if (typeof metrics !== "object" || metrics === null) return undefined; + const metricKey = dataKey.substring(dataKey.indexOf(".") + 1); + const value = (metrics as Record)[metricKey]; + return typeof value === "number" ? value : undefined; +}; + +const formatMetricValue = (rawValue: number | undefined, isSpend: boolean): string => { + if (rawValue === undefined) return "N/A"; + if (isSpend) return `$${rawValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; + return rawValue.toLocaleString(); +}; + +export const CustomTooltip = ({ active, payload, label }: ChartTooltipProps) => { + if (!active || !payload || payload.length === 0) return null; + + return ( +
+

{label == null ? "" : String(label)}

+ {payload.map((item) => { + const dataKey = item.dataKey?.toString(); + if (!dataKey || !item.payload) return null; + + const formattedValue = formatMetricValue(rawMetricValue(item.payload, dataKey), dataKey.includes("spend")); + + return ( +
+
+ +

{formatCategoryName(dataKey)}

+
+

{formattedValue}

+
+ ); + })} +
+ ); +}; diff --git a/ui/litellm-dashboard/src/components/shared/charts/colors.ts b/ui/litellm-dashboard/src/components/shared/charts/colors.ts new file mode 100644 index 00000000000..c30f58e9e4d --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/colors.ts @@ -0,0 +1,58 @@ +export const CHART_COLOR_HEX = { + slate: "#64748b", + gray: "#6b7280", + zinc: "#71717a", + neutral: "#737373", + stone: "#78716c", + red: "#ef4444", + orange: "#f97316", + amber: "#f59e0b", + yellow: "#eab308", + lime: "#84cc16", + green: "#22c55e", + emerald: "#10b981", + teal: "#14b8a6", + cyan: "#06b6d4", + sky: "#0ea5e9", + blue: "#3b82f6", + indigo: "#6366f1", + violet: "#8b5cf6", + purple: "#a855f7", + fuchsia: "#d946ef", + pink: "#ec4899", + rose: "#f43f5e", +} as const; + +export type ChartColor = keyof typeof CHART_COLOR_HEX; + +export const DEFAULT_COLOR_CYCLE: readonly ChartColor[] = [ + "blue", + "cyan", + "sky", + "indigo", + "violet", + "purple", + "fuchsia", + "slate", + "gray", + "zinc", + "neutral", + "stone", + "red", + "orange", + "amber", + "yellow", + "lime", + "green", + "emerald", + "teal", + "pink", + "rose", +]; + +export const chartColorValue = (color: ChartColor): string => `var(--color-${color}-500, ${CHART_COLOR_HEX[color]})`; + +export const categoryFills = (count: number, colors?: readonly ChartColor[]): readonly string[] => { + const cycle = colors && colors.length > 0 ? colors : DEFAULT_COLOR_CYCLE; + return Array.from({ length: count }, (_, i) => chartColorValue(cycle[i % cycle.length])); +}; diff --git a/ui/litellm-dashboard/src/components/shared/charts/donut_chart.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/donut_chart.test.tsx new file mode 100644 index 00000000000..123c6cad0ec --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/donut_chart.test.tsx @@ -0,0 +1,38 @@ +import { render } from "@testing-library/react"; +import React from "react"; +import { describe, expect, it } from "vitest"; +import { DonutChart } from "./donut_chart"; + +const data = [ + { provider: "openai", spend: 40 }, + { provider: "anthropic", spend: 30 }, + { provider: "bedrock", spend: 20 }, +]; + +describe("DonutChart", () => { + it("renders one sector per datum, cycling the given colors", () => { + const { container } = render( + , + ); + + const sectors = Array.from(container.querySelectorAll(".recharts-pie-sector path")); + expect(sectors).toHaveLength(3); + expect(sectors.map((sector) => sector.getAttribute("fill"))).toEqual([ + "var(--color-cyan-500, #06b6d4)", + "var(--color-blue-500, #3b82f6)", + "var(--color-cyan-500, #06b6d4)", + ]); + }); + + it("renders a full pie when variant is pie and a hollow donut otherwise", () => { + const { container: donut } = render(); + const { container: pie } = render( + , + ); + + const donutPath = donut.querySelector(".recharts-pie-sector path")?.getAttribute("d") ?? ""; + const piePath = pie.querySelector(".recharts-pie-sector path")?.getAttribute("d") ?? ""; + expect(donutPath).not.toEqual(piePath); + expect((donutPath.match(/A/g) ?? []).length).toBeGreaterThan((piePath.match(/A/g) ?? []).length); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/charts/donut_chart.tsx b/ui/litellm-dashboard/src/components/shared/charts/donut_chart.tsx new file mode 100644 index 00000000000..c2ce8c02e35 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/donut_chart.tsx @@ -0,0 +1,67 @@ +"use client"; + +import * as React from "react"; +import { Cell, Pie, PieChart } from "recharts"; +import { ChartContainer, ChartTooltip, type ChartConfig } from "@/components/ui/chart"; +import { cn } from "@/lib/cva.config"; +import { ValueTooltip } from "./chart_tooltip"; +import { categoryFills, type ChartColor } from "./colors"; + +export type DonutChartProps> = { + data: readonly TDatum[]; + index: string; + category: string; + colors?: readonly ChartColor[]; + variant?: "donut" | "pie"; + valueFormatter?: (value: number) => string; + showTooltip?: boolean; + className?: string; + style?: React.CSSProperties; +}; + +export function DonutChart>({ + data, + index, + category, + colors, + variant = "donut", + valueFormatter, + showTooltip = true, + className, + style, +}: DonutChartProps) { + const fills = categoryFills(data.length, colors); + const config: ChartConfig = Object.fromEntries( + data.map((datum, i) => { + const name = String(datum[index] ?? i); + return [name, { label: name }]; + }), + ); + + return ( + + + {showTooltip && ( + ( + + )} + /> + )} + + {data.map((datum, i) => ( + + ))} + + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/charts/index.ts b/ui/litellm-dashboard/src/components/shared/charts/index.ts new file mode 100644 index 00000000000..ba0a7544ddb --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/charts/index.ts @@ -0,0 +1,12 @@ +export { AreaChart, type AreaChartProps } from "./area_chart"; +export { BarChart, type BarChartProps } from "./bar_chart"; +export { CustomLegend } from "./chart_legend"; +export { + CustomTooltip, + ValueTooltip, + formatCategoryName, + type ChartTooltipComponent, + type ChartTooltipProps, +} from "./chart_tooltip"; +export { CHART_COLOR_HEX, DEFAULT_COLOR_CYCLE, categoryFills, chartColorValue, type ChartColor } from "./colors"; +export { DonutChart, type DonutChartProps } from "./donut_chart"; diff --git a/ui/litellm-dashboard/src/components/ui/card.tsx b/ui/litellm-dashboard/src/components/ui/card.tsx new file mode 100644 index 00000000000..3fc0aa65264 --- /dev/null +++ b/ui/litellm-dashboard/src/components/ui/card.tsx @@ -0,0 +1,86 @@ +import * as React from "react"; + +import { cn } from "@/lib/cva.config"; + +const Card = React.forwardRef & { size?: "default" | "sm" }>( + ({ className, size = "default", ...props }, ref) => ( +
img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", + className, + )} + {...props} + /> + ), +); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardDescription.displayName = "CardDescription"; + +const CardAction = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardAction.displayName = "CardAction"; + +const CardContent = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardFooter.displayName = "CardFooter"; + +export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent }; diff --git a/ui/litellm-dashboard/src/components/ui/chart.test.tsx b/ui/litellm-dashboard/src/components/ui/chart.test.tsx new file mode 100644 index 00000000000..8b70a6e3246 --- /dev/null +++ b/ui/litellm-dashboard/src/components/ui/chart.test.tsx @@ -0,0 +1,38 @@ +import { render } from "@testing-library/react"; +import * as React from "react"; +import { describe, expect, it } from "vitest"; +import { ChartContainer } from "./chart"; + +describe("ChartStyle hardening", () => { + it("sanitizes config keys and strips structural characters from color values", () => { + const { container } = render( + " }, + }} + > + + , + ); + + const style = container.querySelector("style"); + expect(style).not.toBeNull(); + const css = style!.innerHTML; + + expect(css).toContain("--color-metrics_total_tokens: var(--color-blue-500, #3b82f6);"); + expect(css).not.toContain("metrics.total_tokens"); + expect(css).toContain("--color-evil_key:"); + expect(css).not.toContain("<"); + expect((css.match(/{/g) ?? []).length).toBe((css.match(/}/g) ?? []).length); + }); + + it("emits no style tag when no config entry has a color", () => { + const { container } = render( + + + , + ); + expect(container.querySelector("style")).toBeNull(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/ui/chart.tsx b/ui/litellm-dashboard/src/components/ui/chart.tsx new file mode 100644 index 00000000000..14e10b9f06f --- /dev/null +++ b/ui/litellm-dashboard/src/components/ui/chart.tsx @@ -0,0 +1,324 @@ +"use client"; + +import * as React from "react"; +import * as RechartsPrimitive from "recharts"; +import type { TooltipValueType } from "recharts"; + +import { cn } from "@/lib/cva.config"; + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const; + +const INITIAL_DIMENSION = { width: 320, height: 200 } as const; +type TooltipNameType = number | string; + +export type ChartConfig = Record< + string, + { + label?: React.ReactNode; + icon?: React.ComponentType; + } & ({ color?: string; theme?: never } | { color?: never; theme: Record }) +>; + +type ChartContextProps = { + config: ChartConfig; +}; + +const ChartContext = React.createContext(null); + +function useChart() { + const context = React.useContext(ChartContext); + + if (!context) { + throw new Error("useChart must be used within a "); + } + + return context; +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef<"div"> & { + config: ChartConfig; + children: React.ComponentProps["children"]; + initialDimension?: { + width: number; + height: number; + }; + } +>(({ id, className, children, config, initialDimension = INITIAL_DIMENSION, ...props }, ref) => { + const uniqueId = React.useId(); + const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`; + + return ( + +
+ + + {children} + +
+
+ ); +}); +ChartContainer.displayName = "ChartContainer"; + +const cssVarName = (key: string) => key.replace(/[^a-zA-Z0-9_-]/g, "_"); +const cssColorValue = (color: string) => color.replace(/[;{}<>]/g, ""); + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter(([, config]) => config.theme ?? config.color); + + if (!colorConfig.length) { + return null; + } + + return ( +

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/404/index.html b/litellm/proxy/_experimental/out/404/index.html index 4009a7f4b95..7d4cc0b67af 100644 --- a/litellm/proxy/_experimental/out/404/index.html +++ b/litellm/proxy/_experimental/out/404/index.html @@ -1 +1 @@ -404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt index 6aa34991087..b87b291253e 100644 --- a/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt +++ b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt @@ -1,9 +1,9 @@ 1:"$Sreact.fragment" -2:I[347257,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"ClientPageRoot"] -3:I[871135,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js","/litellm-asset-prefix/_next/static/chunks/0uuigwiz-in3~.js","/litellm-asset-prefix/_next/static/chunks/0-0c4mv4-mc9n.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0p3v32gvsxp6h.js","/litellm-asset-prefix/_next/static/chunks/0x09ws363q4_0.js","/litellm-asset-prefix/_next/static/chunks/08o64zaid_juv.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/0g9k1~ppf2hw3.js","/litellm-asset-prefix/_next/static/chunks/151r-5htw45m~.js","/litellm-asset-prefix/_next/static/chunks/0v85l0arelm41.js","/litellm-asset-prefix/_next/static/chunks/0zbgu4ogb6mba.js","/litellm-asset-prefix/_next/static/chunks/0ae3np_qb52e-.js","/litellm-asset-prefix/_next/static/chunks/0zam.8alu6_vj.js","/litellm-asset-prefix/_next/static/chunks/0uu6lckpr0s15.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0-s2am3eulbyd.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.w8~sa9q0n_s.js","/litellm-asset-prefix/_next/static/chunks/00q4mtjboprhm.js","/litellm-asset-prefix/_next/static/chunks/0zrbitbm~0koh.js","/litellm-asset-prefix/_next/static/chunks/0c4pfjjue0uc-.js","/litellm-asset-prefix/_next/static/chunks/0efmbzvj03niy.js","/litellm-asset-prefix/_next/static/chunks/055egae-ggkjh.js","/litellm-asset-prefix/_next/static/chunks/0hwip5a7qsmis.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js"],"default"] -6:I[897367,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"OutletBoundary"] +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[871135,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] 7:"$Sreact.suspense" -0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0ae3np_qb52e-.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0zam.8alu6_vj.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0uu6lckpr0s15.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0-s2am3eulbyd.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.w8~sa9q0n_s.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/00q4mtjboprhm.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zrbitbm~0koh.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0c4pfjjue0uc-.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0efmbzvj03niy.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/055egae-ggkjh.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0hwip5a7qsmis.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"KYqiq5stbD-H4YcZ-6OuP"} +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} 4:{} 5:"$0:rsc:props:children:0:props:serverProvidedParams:params" 8:null diff --git a/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt index b52b61e168b..3413c4c285d 100644 --- a/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt +++ b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt @@ -1,7 +1,7 @@ 1:"$Sreact.fragment" -2:I[92825,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"ClientSegmentRoot"] -3:I[216370,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js","/litellm-asset-prefix/_next/static/chunks/0uuigwiz-in3~.js","/litellm-asset-prefix/_next/static/chunks/0-0c4mv4-mc9n.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0p3v32gvsxp6h.js","/litellm-asset-prefix/_next/static/chunks/0x09ws363q4_0.js","/litellm-asset-prefix/_next/static/chunks/08o64zaid_juv.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/0g9k1~ppf2hw3.js","/litellm-asset-prefix/_next/static/chunks/151r-5htw45m~.js","/litellm-asset-prefix/_next/static/chunks/0v85l0arelm41.js","/litellm-asset-prefix/_next/static/chunks/0zbgu4ogb6mba.js"],"default"] -4:I[339756,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -5:I[837457,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0uuigwiz-in3~.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0-0c4mv4-mc9n.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0p3v32gvsxp6h.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0x09ws363q4_0.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/08o64zaid_juv.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0g9k1~ppf2hw3.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/151r-5htw45m~.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0v85l0arelm41.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zbgu4ogb6mba.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"KYqiq5stbD-H4YcZ-6OuP"} +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} 6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/__next._full.txt b/litellm/proxy/_experimental/out/__next._full.txt index bb67fb01bc2..8aebbcdc258 100644 --- a/litellm/proxy/_experimental/out/__next._full.txt +++ b/litellm/proxy/_experimental/out/__next._full.txt @@ -1,30 +1,31 @@ 1:"$Sreact.fragment" -2:I[867271,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -3:I[71195,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -4:I[557951,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"AuthProvider"] -5:I[339756,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -6:I[837457,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -7:I[92825,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"ClientSegmentRoot"] -8:I[216370,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js","/litellm-asset-prefix/_next/static/chunks/0uuigwiz-in3~.js","/litellm-asset-prefix/_next/static/chunks/0-0c4mv4-mc9n.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0p3v32gvsxp6h.js","/litellm-asset-prefix/_next/static/chunks/0x09ws363q4_0.js","/litellm-asset-prefix/_next/static/chunks/08o64zaid_juv.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/0g9k1~ppf2hw3.js","/litellm-asset-prefix/_next/static/chunks/151r-5htw45m~.js","/litellm-asset-prefix/_next/static/chunks/0v85l0arelm41.js","/litellm-asset-prefix/_next/static/chunks/0zbgu4ogb6mba.js"],"default"] -c:I[168027,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default",1] +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +d:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] :HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] -:HL["/litellm-asset-prefix/_next/static/chunks/075sund.-mh4~.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] :HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/075sund.-mh4~.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0uuigwiz-in3~.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0-0c4mv4-mc9n.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0p3v32gvsxp6h.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0x09ws363q4_0.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/08o64zaid_juv.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0g9k1~ppf2hw3.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/151r-5htw45m~.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0v85l0arelm41.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zbgu4ogb6mba.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{},null,false,null]},null,false,null]},null,false,null],"$Lb",false]],"m":"$undefined","G":["$c",["$Ld","$Le"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"KYqiq5stbD-H4YcZ-6OuP"} -f:I[347257,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"ClientPageRoot"] -10:I[871135,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js","/litellm-asset-prefix/_next/static/chunks/0uuigwiz-in3~.js","/litellm-asset-prefix/_next/static/chunks/0-0c4mv4-mc9n.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0p3v32gvsxp6h.js","/litellm-asset-prefix/_next/static/chunks/0x09ws363q4_0.js","/litellm-asset-prefix/_next/static/chunks/08o64zaid_juv.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/0g9k1~ppf2hw3.js","/litellm-asset-prefix/_next/static/chunks/151r-5htw45m~.js","/litellm-asset-prefix/_next/static/chunks/0v85l0arelm41.js","/litellm-asset-prefix/_next/static/chunks/0zbgu4ogb6mba.js","/litellm-asset-prefix/_next/static/chunks/0ae3np_qb52e-.js","/litellm-asset-prefix/_next/static/chunks/0zam.8alu6_vj.js","/litellm-asset-prefix/_next/static/chunks/0uu6lckpr0s15.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0-s2am3eulbyd.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.w8~sa9q0n_s.js","/litellm-asset-prefix/_next/static/chunks/00q4mtjboprhm.js","/litellm-asset-prefix/_next/static/chunks/0zrbitbm~0koh.js","/litellm-asset-prefix/_next/static/chunks/0c4pfjjue0uc-.js","/litellm-asset-prefix/_next/static/chunks/0efmbzvj03niy.js","/litellm-asset-prefix/_next/static/chunks/055egae-ggkjh.js","/litellm-asset-prefix/_next/static/chunks/0hwip5a7qsmis.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js"],"default"] -13:I[897367,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"OutletBoundary"] -14:"$Sreact.suspense" -16:I[897367,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"ViewportBoundary"] -18:I[897367,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"MetadataBoundary"] -a:["$","$1","c",{"children":[["$","$Lf",null,{"Component":"$10","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@11","$@12"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0ae3np_qb52e-.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0zam.8alu6_vj.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0uu6lckpr0s15.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0-s2am3eulbyd.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.w8~sa9q0n_s.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/00q4mtjboprhm.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zrbitbm~0koh.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0c4pfjjue0uc-.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0efmbzvj03niy.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/055egae-ggkjh.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0hwip5a7qsmis.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}]],["$","$L13",null,{"children":["$","$14",null,{"name":"Next.MetadataOutlet","children":"$@15"}]}]]}] -b:["$","$1","h",{"children":[null,["$","$L16",null,{"children":"$L17"}],["$","div",null,{"hidden":true,"children":["$","$L18",null,{"children":["$","$14",null,{"name":"Next.Metadata","children":"$L19"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] -d:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] -e:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/075sund.-mh4~.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] -9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" -11:{} -12:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" -17:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] -1a:I[27201,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"IconMark"] -15:null -19:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1a","4",{}]] +0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{},null,false,null]},null,false,null]},null,false,null],"$Lc",false]],"m":"$undefined","G":["$d",["$Le","$Lf"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +10:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +11:I[871135,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js"],"default"] +14:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +15:"$Sreact.suspense" +17:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[["$","$L10",null,{"Component":"$11","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@12","$@13"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}]],["$","$L14",null,{"children":["$","$15",null,{"name":"Next.MetadataOutlet","children":"$@16"}]}]]}] +c:["$","$1","h",{"children":[null,["$","$L17",null,{"children":"$L18"}],["$","div",null,{"hidden":true,"children":["$","$L19",null,{"children":["$","$15",null,{"name":"Next.Metadata","children":"$L1a"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +e:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +f:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +12:{} +13:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +18:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1b:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +16:null +1a:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1b","4",{}]] diff --git a/litellm/proxy/_experimental/out/__next._head.txt b/litellm/proxy/_experimental/out/__next._head.txt index c896283665a..51067b68caa 100644 --- a/litellm/proxy/_experimental/out/__next._head.txt +++ b/litellm/proxy/_experimental/out/__next._head.txt @@ -1,6 +1,6 @@ 1:"$Sreact.fragment" -2:I[897367,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"ViewportBoundary"] -3:I[897367,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"MetadataBoundary"] +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] 4:"$Sreact.suspense" -5:I[27201,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"IconMark"] -0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"KYqiq5stbD-H4YcZ-6OuP"} +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/__next._index.txt b/litellm/proxy/_experimental/out/__next._index.txt index e21c0fe74b8..ac9a9fe0dca 100644 --- a/litellm/proxy/_experimental/out/__next._index.txt +++ b/litellm/proxy/_experimental/out/__next._index.txt @@ -1,9 +1,9 @@ 1:"$Sreact.fragment" -2:I[867271,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -3:I[71195,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -4:I[557951,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"AuthProvider"] -5:I[339756,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] -6:I[837457,["/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js"],"default"] +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] :HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] -:HL["/litellm-asset-prefix/_next/static/chunks/075sund.-mh4~.css","style"] -0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/075sund.-mh4~.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/08yy42xvwaak6.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9hs7onyj28m.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0pidya1qvuvx8.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"KYqiq5stbD-H4YcZ-6OuP"} +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/__next._tree.txt b/litellm/proxy/_experimental/out/__next._tree.txt index 843f0806214..70be0036004 100644 --- a/litellm/proxy/_experimental/out/__next._tree.txt +++ b/litellm/proxy/_experimental/out/__next._tree.txt @@ -1,4 +1,4 @@ :HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] -:HL["/litellm-asset-prefix/_next/static/chunks/075sund.-mh4~.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] :HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"KYqiq5stbD-H4YcZ-6OuP"} +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/_next/static/KYqiq5stbD-H4YcZ-6OuP/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/KYqiq5stbD-H4YcZ-6OuP/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/KYqiq5stbD-H4YcZ-6OuP/_clientMiddlewareManifest.js b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_clientMiddlewareManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/KYqiq5stbD-H4YcZ-6OuP/_clientMiddlewareManifest.js rename to litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_clientMiddlewareManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/KYqiq5stbD-H4YcZ-6OuP/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/KYqiq5stbD-H4YcZ-6OuP/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-.xiiczht-vh.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-.xiiczht-vh.js deleted file mode 100644 index 9d2f975ff66..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0-.xiiczht-vh.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(829087),o=e.i(480731),l=e.i(444755),d=e.i(673706),s=e.i(95779);let n={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},i={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},m={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},c=(0,d.makeClassName)("Icon"),g=t.default.forwardRef((e,g)=>{let{icon:u,variant:b="simple",tooltip:f,size:h=o.Sizes.SM,color:w,className:C}=e,k=(0,r.__rest)(e,["icon","variant","tooltip","size","color","className"]),p=((e,r)=>{switch(e){case"simple":return{textColor:r?(0,d.getColorClassNames)(r,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:r?(0,d.getColorClassNames)(r,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,l.tremorTwMerge)((0,d.getColorClassNames)(r,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:r?(0,d.getColorClassNames)(r,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,l.tremorTwMerge)((0,d.getColorClassNames)(r,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:r?(0,d.getColorClassNames)(r,s.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,l.tremorTwMerge)((0,d.getColorClassNames)(r,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:r?(0,d.getColorClassNames)(r,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,l.tremorTwMerge)((0,d.getColorClassNames)(r,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:r?(0,d.getColorClassNames)(r,s.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:r?(0,l.tremorTwMerge)((0,d.getColorClassNames)(r,s.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(b,w),{tooltipProps:x,getReferenceProps:N}=(0,a.useTooltip)();return t.default.createElement("span",Object.assign({ref:(0,d.mergeRefs)([g,x.refs.setReference]),className:(0,l.tremorTwMerge)(c("root"),"inline-flex shrink-0 items-center justify-center",p.bgColor,p.textColor,p.borderColor,p.ringColor,m[b].rounded,m[b].border,m[b].shadow,m[b].ring,n[h].paddingX,n[h].paddingY,C)},N,k),t.default.createElement(a.default,Object.assign({text:f},x)),t.default.createElement(u,{className:(0,l.tremorTwMerge)(c("icon"),"shrink-0",i[h].height,i[h].width)}))});g.displayName="Icon",e.s(["default",0,g],728889)},752978,e=>{"use strict";var r=e.i(728889);e.s(["Icon",()=>r.default])},871943,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,t],871943)},360820,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,t],360820)},269200,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=t.default.forwardRef((e,l)=>{let{children:d,className:s}=e,n=(0,r.__rest)(e,["children","className"]);return t.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",s)},t.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},n),d))});l.displayName="Table",e.s(["Table",0,l],269200)},427612,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=t.default.forwardRef((e,l)=>{let{children:d,className:s}=e,n=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",s)},n),d))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=t.default.forwardRef((e,l)=>{let{children:d,className:s}=e,n=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",s)},n),d))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},942232,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=t.default.forwardRef((e,l)=>{let{children:d,className:s}=e,n=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",s)},n),d))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},496020,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=t.default.forwardRef((e,l)=>{let{children:d,className:s}=e,n=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),s)},n),d))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},977572,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=t.default.forwardRef((e,l)=>{let{children:d,className:s}=e,n=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",s)},n),d))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},68155,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,t],68155)},278587,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,t],278587)},973095,e=>{"use strict";var r=e.i(843476),t=e.i(502501),a=e.i(135214),o=e.i(936578),l=e.i(271645);function d(){let{isLoading:e,isAuthorized:l}=(0,a.default)();return e||!l?(0,r.jsx)(o.default,{}):(0,r.jsx)(t.default,{})}e.s(["default",0,function(){return(0,r.jsx)(l.Suspense,{fallback:(0,r.jsx)(o.default,{}),children:(0,r.jsx)(d,{})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-0c4mv4-mc9n.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-0c4mv4-mc9n.js deleted file mode 100644 index 8f16e50edb1..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0-0c4mv4-mc9n.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,186312,e=>{"use strict";var t=new WeakMap,r=new WeakMap,n={},s=0,o=function(e){return e&&(e.host||o(e.parentNode))},i=function(e,i,a,l){var u=(Array.isArray(e)?e:[e]).map(function(e){if(i.contains(e))return e;var t=o(e);return t&&i.contains(t)?t:(console.error("aria-hidden",e,"in not contained inside",i,". Doing nothing"),null)}).filter(function(e){return!!e});n[a]||(n[a]=new WeakMap);var c=n[a],d=[],h=new Set,p=new Set(u),f=function(e){!e||h.has(e)||(h.add(e),f(e.parentNode))};u.forEach(f);var m=function(e){!e||p.has(e)||Array.prototype.forEach.call(e.children,function(e){if(h.has(e))m(e);else try{var n=e.getAttribute(l),s=null!==n&&"false"!==n,o=(t.get(e)||0)+1,i=(c.get(e)||0)+1;t.set(e,o),c.set(e,i),d.push(e),1===o&&s&&r.set(e,!0),1===i&&e.setAttribute(a,"true"),s||e.setAttribute(l,"true")}catch(t){console.error("aria-hidden: cannot operate on ",e,t)}})};return m(i),h.clear(),s++,function(){d.forEach(function(e){var n=t.get(e)-1,s=c.get(e)-1;t.set(e,n),c.set(e,s),n||(r.has(e)||e.removeAttribute(l),r.delete(e)),s||e.removeAttribute(a)}),--s||(t=new WeakMap,t=new WeakMap,r=new WeakMap,n={})}};e.s(["hideOthers",0,function(e,t,r){void 0===r&&(r="data-aria-hidden");var n=Array.from(Array.isArray(e)?e:[e]),s=t||("u"{t.exports=e.r(976562)},266027,869230,469637,e=>{"use strict";let t;var r=e.i(175555),n=e.i(273911),s=e.i(540143),o=e.i(286491),i=e.i(915823),a=e.i(793803),l=e.i(619273),u=e.i(180166),c=class extends i.Subscribable{constructor(e,t){super(),this.options=t,this.#e=e,this.#t=null,this.#r=(0,a.pendingThenable)(),this.bindMethods(),this.setOptions(t)}#e;#n=void 0;#s=void 0;#o=void 0;#i;#a;#r;#t;#l;#u;#c;#d;#h;#p;#f=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){1===this.listeners.size&&(this.#n.addObserver(this),d(this.#n,this.options)?this.#m():this.updateResult(),this.#g())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return h(this.#n,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return h(this.#n,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#y(),this.#b(),this.#n.removeObserver(this)}setOptions(e){let t=this.options,r=this.#n;if(this.options=this.#e.defaultQueryOptions(e),void 0!==this.options.enabled&&"boolean"!=typeof this.options.enabled&&"function"!=typeof this.options.enabled&&"boolean"!=typeof(0,l.resolveQueryBoolean)(this.options.enabled,this.#n))throw Error("Expected enabled to be a boolean or a callback that returns a boolean");this.#v(),this.#n.setOptions(this.options),t._defaulted&&!(0,l.shallowEqualObjects)(this.options,t)&&this.#e.getQueryCache().notify({type:"observerOptionsUpdated",query:this.#n,observer:this});let n=this.hasListeners();n&&p(this.#n,r,this.options,t)&&this.#m(),this.updateResult(),n&&(this.#n!==r||(0,l.resolveQueryBoolean)(this.options.enabled,this.#n)!==(0,l.resolveQueryBoolean)(t.enabled,this.#n)||(0,l.resolveStaleTime)(this.options.staleTime,this.#n)!==(0,l.resolveStaleTime)(t.staleTime,this.#n))&&this.#C();let s=this.#R();n&&(this.#n!==r||(0,l.resolveQueryBoolean)(this.options.enabled,this.#n)!==(0,l.resolveQueryBoolean)(t.enabled,this.#n)||s!==this.#p)&&this.#S(s)}getOptimisticResult(e){var t,r;let n=this.#e.getQueryCache().build(this.#e,e),s=this.createResult(n,e);return t=this,r=s,(0,l.shallowEqualObjects)(t.getCurrentResult(),r)||(this.#o=s,this.#a=this.options,this.#i=this.#n.state),s}getCurrentResult(){return this.#o}trackResult(e,t){return new Proxy(e,{get:(e,r)=>(this.trackProp(r),t?.(r),"promise"===r&&(this.trackProp("data"),this.options.experimental_prefetchInRender||"pending"!==this.#r.status||this.#r.reject(Error("experimental_prefetchInRender feature flag is not enabled"))),Reflect.get(e,r))})}trackProp(e){this.#f.add(e)}getCurrentQuery(){return this.#n}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),r=this.#e.getQueryCache().build(this.#e,t);return r.fetch().then(()=>this.createResult(r,t))}fetch(e){return this.#m({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#o))}#m(e){this.#v();let t=this.#n.fetch(this.options,e);return e?.throwOnError||(t=t.catch(l.noop)),t}#C(){this.#y();let e=(0,l.resolveStaleTime)(this.options.staleTime,this.#n);if(n.environmentManager.isServer()||this.#o.isStale||!(0,l.isValidTimeout)(e))return;let t=(0,l.timeUntilStale)(this.#o.dataUpdatedAt,e);this.#d=u.timeoutManager.setTimeout(()=>{this.#o.isStale||this.updateResult()},t+1)}#R(){return("function"==typeof this.options.refetchInterval?this.options.refetchInterval(this.#n):this.options.refetchInterval)??!1}#S(e){this.#b(),this.#p=e,!n.environmentManager.isServer()&&!1!==(0,l.resolveQueryBoolean)(this.options.enabled,this.#n)&&(0,l.isValidTimeout)(this.#p)&&0!==this.#p&&(this.#h=u.timeoutManager.setInterval(()=>{(this.options.refetchIntervalInBackground||r.focusManager.isFocused())&&this.#m()},this.#p))}#g(){this.#C(),this.#S(this.#R())}#y(){void 0!==this.#d&&(u.timeoutManager.clearTimeout(this.#d),this.#d=void 0)}#b(){void 0!==this.#h&&(u.timeoutManager.clearInterval(this.#h),this.#h=void 0)}createResult(e,t){let r,n=this.#n,s=this.options,i=this.#o,u=this.#i,c=this.#a,h=e!==n?e.state:this.#s,{state:m}=e,g={...m},y=!1;if(t._optimisticResults){let r=this.hasListeners(),i=!r&&d(e,t),a=r&&p(e,n,t,s);(i||a)&&(g={...g,...(0,o.fetchState)(m.data,e.options)}),"isRestoring"===t._optimisticResults&&(g.fetchStatus="idle")}let{error:b,errorUpdatedAt:v,status:C}=g;r=g.data;let R=!1;if(void 0!==t.placeholderData&&void 0===r&&"pending"===C){let e;i?.isPlaceholderData&&t.placeholderData===c?.placeholderData?(e=i.data,R=!0):e="function"==typeof t.placeholderData?t.placeholderData(this.#c?.state.data,this.#c):t.placeholderData,void 0!==e&&(C="success",r=(0,l.replaceData)(i?.data,e,t),y=!0)}if(t.select&&void 0!==r&&!R)if(i&&r===u?.data&&t.select===this.#l)r=this.#u;else try{this.#l=t.select,r=t.select(r),r=(0,l.replaceData)(i?.data,r,t),this.#u=r,this.#t=null}catch(e){this.#t=e}this.#t&&(b=this.#t,r=this.#u,v=Date.now(),C="error");let S="fetching"===g.fetchStatus,O="pending"===C,w="error"===C,k=O&&S,I=void 0!==r,x={status:C,fetchStatus:g.fetchStatus,isPending:O,isSuccess:"success"===C,isError:w,isInitialLoading:k,isLoading:k,data:r,dataUpdatedAt:g.dataUpdatedAt,error:b,errorUpdatedAt:v,failureCount:g.fetchFailureCount,failureReason:g.fetchFailureReason,errorUpdateCount:g.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:g.dataUpdateCount>h.dataUpdateCount||g.errorUpdateCount>h.errorUpdateCount,isFetching:S,isRefetching:S&&!O,isLoadingError:w&&!I,isPaused:"paused"===g.fetchStatus,isPlaceholderData:y,isRefetchError:w&&I,isStale:f(e,t),refetch:this.refetch,promise:this.#r,isEnabled:!1!==(0,l.resolveQueryBoolean)(t.enabled,e)};if(this.options.experimental_prefetchInRender){let t=void 0!==x.data,r="error"===x.status&&!t,s=e=>{r?e.reject(x.error):t&&e.resolve(x.data)},o=()=>{s(this.#r=x.promise=(0,a.pendingThenable)())},i=this.#r;switch(i.status){case"pending":e.queryHash===n.queryHash&&s(i);break;case"fulfilled":(r||x.data!==i.value)&&o();break;case"rejected":r&&x.error===i.reason||o()}}return x}updateResult(){let e=this.#o,t=this.createResult(this.#n,this.options);if(this.#i=this.#n.state,this.#a=this.options,void 0!==this.#i.data&&(this.#c=this.#n),(0,l.shallowEqualObjects)(t,e))return;this.#o=t;let r=()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,r="function"==typeof t?t():t;if("all"===r||!r&&!this.#f.size)return!0;let n=new Set(r??this.#f);return this.options.throwOnError&&n.add("error"),Object.keys(this.#o).some(t=>this.#o[t]!==e[t]&&n.has(t))};this.#O({listeners:r()})}#v(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#n)return;let t=this.#n;this.#n=e,this.#s=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#g()}#O(e){s.notifyManager.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#o)}),this.#e.getQueryCache().notify({query:this.#n,type:"observerResultsUpdated"})})}};function d(e,t){return!1!==(0,l.resolveQueryBoolean)(t.enabled,e)&&void 0===e.state.data&&("error"!==e.state.status||!1!==(0,l.resolveQueryBoolean)(t.retryOnMount,e))||void 0!==e.state.data&&h(e,t,t.refetchOnMount)}function h(e,t,r){if(!1!==(0,l.resolveQueryBoolean)(t.enabled,e)&&"static"!==(0,l.resolveStaleTime)(t.staleTime,e)){let n="function"==typeof r?r(e):r;return"always"===n||!1!==n&&f(e,t)}return!1}function p(e,t,r,n){return(e!==t||!1===(0,l.resolveQueryBoolean)(n.enabled,e))&&(!r.suspense||"error"!==e.state.status)&&f(e,r)}function f(e,t){return!1!==(0,l.resolveQueryBoolean)(t.enabled,e)&&e.isStaleByTime((0,l.resolveStaleTime)(t.staleTime,e))}e.s(["QueryObserver",0,c],869230),e.i(247167);var m=e.i(271645),g=e.i(912598);e.i(843476);var y=m.createContext((t=!1,{clearReset:()=>{t=!1},reset:()=>{t=!0},isReset:()=>t})),b=m.createContext(!1);b.Provider;var v=(e,t,r)=>t.fetchOptimistic(e).catch(()=>{r.clearReset()});function C(e,t,r){let o,i=m.useContext(b),a=m.useContext(y),u=(0,g.useQueryClient)(r),c=u.defaultQueryOptions(e);u.getDefaultOptions().queries?._experimental_beforeQuery?.(c);let d=u.getQueryCache().get(c.queryHash);if(c._optimisticResults=i?"isRestoring":"optimistic",c.suspense){let e=e=>"static"===e?e:Math.max(e??1e3,1e3),t=c.staleTime;c.staleTime="function"==typeof t?(...r)=>e(t(...r)):e(t),"number"==typeof c.gcTime&&(c.gcTime=Math.max(c.gcTime,1e3))}o=d?.state.error&&"function"==typeof c.throwOnError?(0,l.shouldThrowError)(c.throwOnError,[d.state.error,d]):c.throwOnError,(c.suspense||c.experimental_prefetchInRender||o)&&!a.isReset()&&(c.retryOnMount=!1),m.useEffect(()=>{a.clearReset()},[a]);let h=!u.getQueryCache().get(c.queryHash),[p]=m.useState(()=>new t(u,c)),f=p.getOptimisticResult(c),C=!i&&!1!==e.subscribed;if(m.useSyncExternalStore(m.useCallback(e=>{let t=C?p.subscribe(s.notifyManager.batchCalls(e)):l.noop;return p.updateResult(),t},[p,C]),()=>p.getCurrentResult(),()=>p.getCurrentResult()),m.useEffect(()=>{p.setOptions(c)},[c,p]),c?.suspense&&f.isPending)throw v(c,p,a);if((({result:e,errorResetBoundary:t,throwOnError:r,query:n,suspense:s})=>e.isError&&!t.isReset()&&!e.isFetching&&n&&(s&&void 0===e.data||(0,l.shouldThrowError)(r,[e.error,n])))({result:f,errorResetBoundary:a,throwOnError:c.throwOnError,query:d,suspense:c.suspense}))throw f.error;if(u.getDefaultOptions().queries?._experimental_afterQuery?.(c,f),c.experimental_prefetchInRender&&!n.environmentManager.isServer()&&f.isLoading&&f.isFetching&&!i){let e=h?v(c,p,a):d?.promise;e?.catch(l.noop).finally(()=>{p.updateResult()})}return c.notifyOnChangeProps?f:p.trackResult(f)}e.s(["useBaseQuery",0,C],469637),e.s(["useQuery",0,function(e,t){return C(e,c,t)}],266027)},612256,243652,e=>{"use strict";var t=e.i(602869),r=e.i(266027);function n(e){let t=[e];return{all:t,lists:()=>[...t,"list"],list:e=>[...t,"list",{params:e}],details:()=>[...t,"detail"],detail:e=>[...t,"detail",e]}}e.s(["createQueryKeys",0,n],243652);let s=n("uiConfig");e.s(["useUIConfig",0,()=>(0,r.useQuery)({queryKey:s.list({}),queryFn:async()=>await (0,t.getUiConfig)(),staleTime:864e5,gcTime:864e5})],612256)},321836,e=>{"use strict";let t="litellm_return_url",r="redirect_to";function n(){return window.location.href}function s(){if("u"typeof document&&(document.cookie=`${t}=; path=/; max-age=0`)}catch(e){console.error("Failed to clear return URL cookie:",e)}}function i(){return new URLSearchParams(window.location.search).get(r)}function a(){let e=window.location.hostname;return"localhost"===e||"127.0.0.1"===e||"::1"===e||e.startsWith("127.")||e.endsWith(".local")}function l(e){if(!e)return!1;if(e.startsWith("/")&&!e.startsWith("//"))return!0;try{let t=new URL(e),r=window.location.hostname;if(t.hostname!==r)return!1;if(a())return!0;return t.origin===window.location.origin}catch{return!1}}e.s(["buildLoginUrlWithReturn",0,function(e,t){let s=t||n();if(!s||s.includes("/login"))return e;let o=e.includes("?")?"&":"?";return`${e}${o}${r}=${encodeURIComponent(s)}`},"clearStoredReturnUrl",0,o,"consumeReturnUrl",0,function(){let e=i();if(e){if(l(e))return o(),e;a()&&console.warn("[returnUrlUtils] Invalid return URL in params rejected:",e)}let t=s();if(t){if(l(t))return o(),t;a()&&console.warn("[returnUrlUtils] Invalid return URL in cookie rejected:",t)}return null},"getReturnUrl",0,function(){let e=i();if(e)return e;let t=s();return t||null},"isValidReturnUrl",0,l,"normalizeUrlForCompare",0,function(e){try{let t=new URL(e,window.location.origin),r=t.pathname;r.length>1&&r.endsWith("/")&&(r=r.slice(0,-1));let n=new URLSearchParams(t.search),s=new URLSearchParams;Array.from(n.entries()).sort(([e],[t])=>e.localeCompare(t)).forEach(([e,t])=>{s.append(e,t)});let o=s.toString(),i=t.hash||"";return`${t.origin}${r}${o?`?${o}`:""}${i}`}catch{return e}},"storeReturnUrl",0,function(){let e=n();e&&function(e,t,r=300){if("u"{"use strict";var t=e.i(602869),r=e.i(268004),n=e.i(161281),s=e.i(321836),o=e.i(618566),i=e.i(271645),a=e.i(708347),l=e.i(612256);e.s(["default",0,()=>{let e=(0,o.useRouter)(),{data:u,isLoading:c}=(0,l.useUIConfig)(),d="u">typeof document?(0,r.getCookie)("token"):null,h=(0,i.useMemo)(()=>(0,n.decodeToken)(d),[d]),p=(0,i.useMemo)(()=>(0,n.checkTokenValidity)(d),[d])&&!u?.admin_ui_disabled,f=(0,i.useCallback)(()=>{(0,s.storeReturnUrl)();let r=`${(0,t.getProxyBaseUrl)()}/ui/login`,n=(0,s.buildLoginUrlWithReturn)(r);e.replace(n)},[e]);return(0,i.useEffect)(()=>{!c&&(p||(d&&(0,r.clearTokenCookies)(),f()))},[c,p,d,f]),{isLoading:c,isAuthorized:p,token:p?d:null,accessToken:h?.key??null,userId:h?.user_id??null,userEmail:h?.user_email??null,userRole:(0,a.formatUserRole)(h?.user_role),premiumUser:h?.premium_user??null,disabledPersonalKeyCreation:h?.disabled_non_admin_personal_key_creation??null,showSSOBanner:h?.login_method==="username_password"}}])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},563113,887719,e=>{"use strict";var t=e.i(271645),r=e.i(864517),n=e.i(244009),s=e.i(408850),o=e.i(87414);let i=function(...e){let t={};return e.forEach(e=>{e&&Object.keys(e).forEach(r=>{void 0!==e[r]&&(t[r]=e[r])})}),t};function a(e){let{closable:r,closeIcon:n}=e||{};return t.default.useMemo(()=>{if(!r&&(!1===r||!1===n||null===n))return!1;if(void 0===r&&void 0===n)return null;let e={closeIcon:"boolean"!=typeof n&&null!==n?n:void 0};return r&&"object"==typeof r&&(e=Object.assign(Object.assign({},e),r)),e},[r,n])}e.s(["default",0,i],887719);let l={};e.s(["pickClosable",0,function(e){if(!e)return;let{closable:t,closeIcon:r}=e;return{closable:t,closeIcon:r}},"useClosable",0,(e,u,c=l)=>{let d=a(e),h=a(u),[p]=(0,s.useLocale)("global",o.default.global),f="boolean"!=typeof d&&!!(null==d?void 0:d.disabled),m=t.default.useMemo(()=>Object.assign({closeIcon:t.default.createElement(r.default,null)},c),[c]),g=t.default.useMemo(()=>!1!==d&&(d?i(m,h,d):!1!==h&&(h?i(m,h):!!m.closable&&m)),[d,h,m]);return t.default.useMemo(()=>{var e,r;if(!1===g)return[!1,null,f,{}];let{closeIconRender:s}=m,{closeIcon:o}=g,i=o,a=(0,n.default)(g,!0);return null!=i&&(s&&(i=s(o)),i=t.default.isValidElement(i)?t.default.cloneElement(i,Object.assign(Object.assign(Object.assign({},i.props),{"aria-label":null!=(r=null==(e=i.props)?void 0:e["aria-label"])?r:p.close}),a)):t.default.createElement("span",Object.assign({"aria-label":p.close},a),i)),[!0,i,f,a]},[f,p.close,g,m])}],563113)},801312,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"}}]},name:"left",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:n}))});e.s(["default",0,o],801312)},38243,908286,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(876556);function s(e){return["small","middle","large"].includes(e)}function o(e){return!!e&&"number"==typeof e&&!Number.isNaN(e)}e.s(["isPresetSize",0,s,"isValidGapNumber",0,o],908286);var i=e.i(242064),a=e.i(249616),l=e.i(372409),u=e.i(246422);let c=(0,u.genStyleHooks)(["Space","Addon"],e=>[(e=>{let{componentCls:t,borderRadius:r,paddingSM:n,colorBorder:s,paddingXS:o,fontSizeLG:i,fontSizeSM:a,borderRadiusLG:u,borderRadiusSM:c,colorBgContainerDisabled:d,lineWidth:h}=e;return{[t]:[{display:"inline-flex",alignItems:"center",gap:0,paddingInline:n,margin:0,background:d,borderWidth:h,borderStyle:"solid",borderColor:s,borderRadius:r,"&-large":{fontSize:i,borderRadius:u},"&-small":{paddingInline:o,borderRadius:c,fontSize:a},"&-compact-last-item":{borderEndStartRadius:0,borderStartStartRadius:0},"&-compact-first-item":{borderEndEndRadius:0,borderStartEndRadius:0},"&-compact-item:not(:first-child):not(:last-child)":{borderRadius:0},"&-compact-item:not(:last-child)":{borderInlineEndWidth:0}},(0,l.genCompactItemStyle)(e,{focus:!1})]}})(e)]);var d=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var s=0,n=Object.getOwnPropertySymbols(e);st.indexOf(n[s])&&Object.prototype.propertyIsEnumerable.call(e,n[s])&&(r[n[s]]=e[n[s]]);return r};let h=t.default.forwardRef((e,n)=>{let{className:s,children:o,style:l,prefixCls:u}=e,h=d(e,["className","children","style","prefixCls"]),{getPrefixCls:p,direction:f}=t.default.useContext(i.ConfigContext),m=p("space-addon",u),[g,y,b]=c(m),{compactItemClassnames:v,compactSize:C}=(0,a.useCompactItemContext)(m,f),R=(0,r.default)(m,y,v,b,{[`${m}-${C}`]:C},s);return g(t.default.createElement("div",Object.assign({ref:n,className:R,style:l},h),o))}),p=t.default.createContext({latestIndex:0}),f=p.Provider,m=({className:e,index:r,children:n,split:s,style:o})=>{let{latestIndex:i}=t.useContext(p);return null==n?null:t.createElement(t.Fragment,null,t.createElement("div",{className:e,style:o},n),r{let t=(0,g.mergeToken)(e,{spaceGapSmallSize:e.paddingXS,spaceGapMiddleSize:e.padding,spaceGapLargeSize:e.paddingLG});return[(e=>{let{componentCls:t,antCls:r}=e;return{[t]:{display:"inline-flex","&-rtl":{direction:"rtl"},"&-vertical":{flexDirection:"column"},"&-align":{flexDirection:"column","&-center":{alignItems:"center"},"&-start":{alignItems:"flex-start"},"&-end":{alignItems:"flex-end"},"&-baseline":{alignItems:"baseline"}},[`${t}-item:empty`]:{display:"none"},[`${t}-item > ${r}-badge-not-a-wrapper:only-child`]:{display:"block"}}}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-row-small":{rowGap:e.spaceGapSmallSize},"&-gap-row-middle":{rowGap:e.spaceGapMiddleSize},"&-gap-row-large":{rowGap:e.spaceGapLargeSize},"&-gap-col-small":{columnGap:e.spaceGapSmallSize},"&-gap-col-middle":{columnGap:e.spaceGapMiddleSize},"&-gap-col-large":{columnGap:e.spaceGapLargeSize}}}})(t)]},()=>({}),{resetStyle:!1});var b=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var s=0,n=Object.getOwnPropertySymbols(e);st.indexOf(n[s])&&Object.prototype.propertyIsEnumerable.call(e,n[s])&&(r[n[s]]=e[n[s]]);return r};let v=t.forwardRef((e,a)=>{var l;let{getPrefixCls:u,direction:c,size:d,className:h,style:p,classNames:g,styles:v}=(0,i.useComponentConfig)("space"),{size:C=null!=d?d:"small",align:R,className:S,rootClassName:O,children:w,direction:k="horizontal",prefixCls:I,split:x,style:E,wrap:Q=!1,classNames:T,styles:$}=e,B=b(e,["size","align","className","rootClassName","children","direction","prefixCls","split","style","wrap","classNames","styles"]),[j,U]=Array.isArray(C)?C:[C,C],P=s(U),M=s(j),L=o(U),A=o(j),N=(0,n.default)(w,{keepEmpty:!0}),F=void 0===R&&"horizontal"===k?"center":R,_=u("space",I),[z,W,D]=y(_),G=(0,r.default)(_,h,W,`${_}-${k}`,{[`${_}-rtl`]:"rtl"===c,[`${_}-align-${F}`]:F,[`${_}-gap-row-${U}`]:P,[`${_}-gap-col-${j}`]:M},S,O,D),q=(0,r.default)(`${_}-item`,null!=(l=null==T?void 0:T.item)?l:g.item),H=Object.assign(Object.assign({},v.item),null==$?void 0:$.item),V=N.map((e,r)=>{let n=(null==e?void 0:e.key)||`${q}-${r}`;return t.createElement(m,{className:q,key:n,index:r,split:x,style:H},e)}),K=t.useMemo(()=>({latestIndex:N.reduce((e,t,r)=>null!=t?r:e,0)}),[N]);if(0===N.length)return null;let Z={};return Q&&(Z.flexWrap="wrap"),!M&&A&&(Z.columnGap=j),!P&&L&&(Z.rowGap=U),z(t.createElement("div",Object.assign({ref:a,className:G,style:Object.assign(Object.assign(Object.assign({},Z),p),E)},B),t.createElement(f,{value:K},V)))});v.Compact=a.default,v.Addon=h,e.s(["default",0,v],38243)},475254,e=>{"use strict";var t=e.i(271645);let r=e=>{let t=e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,r)=>r?r.toUpperCase():t.toLowerCase());return t.charAt(0).toUpperCase()+t.slice(1)},n=(...e)=>e.filter((e,t,r)=>!!e&&""!==e.trim()&&r.indexOf(e)===t).join(" ").trim();var s={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};let o=(0,t.forwardRef)(({color:e="currentColor",size:r=24,strokeWidth:o=2,absoluteStrokeWidth:i,className:a="",children:l,iconNode:u,...c},d)=>(0,t.createElement)("svg",{ref:d,...s,width:r,height:r,stroke:e,strokeWidth:i?24*Number(o)/Number(r):o,className:n("lucide",a),...!l&&!(e=>{for(let t in e)if(t.startsWith("aria-")||"role"===t||"title"===t)return!0})(c)&&{"aria-hidden":"true"},...c},[...u.map(([e,r])=>(0,t.createElement)(e,r)),...Array.isArray(l)?l:[l]]));e.s(["default",0,(e,s)=>{let i=(0,t.forwardRef)(({className:i,...a},l)=>(0,t.createElement)(o,{ref:l,iconNode:s,className:n(`lucide-${r(e).replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}`,`lucide-${e}`,i),...a}));return i.displayName=r(e),i}],475254)},262218,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(529681),s=e.i(702779),o=e.i(563113),i=e.i(763731),a=e.i(121872),l=e.i(242064);e.i(296059);var u=e.i(915654),c=e.i(135551),d=e.i(183293),h=e.i(246422),p=e.i(838378);let f=e=>{let{lineWidth:t,fontSizeIcon:r,calc:n}=e,s=e.fontSizeSM;return(0,p.mergeToken)(e,{tagFontSize:s,tagLineHeight:(0,u.unit)(n(e.lineHeightSM).mul(s).equal()),tagIconSize:n(r).sub(n(t).mul(2)).equal(),tagPaddingHorizontal:8,tagBorderlessBg:e.defaultBg})},m=e=>({defaultBg:new c.FastColor(e.colorFillQuaternary).onBackground(e.colorBgContainer).toHexString(),defaultColor:e.colorText}),g=(0,h.genStyleHooks)("Tag",e=>(e=>{let{paddingXXS:t,lineWidth:r,tagPaddingHorizontal:n,componentCls:s,calc:o}=e,i=o(n).sub(r).equal(),a=o(t).sub(r).equal();return{[s]:Object.assign(Object.assign({},(0,d.resetComponent)(e)),{display:"inline-block",height:"auto",marginInlineEnd:e.marginXS,paddingInline:i,fontSize:e.tagFontSize,lineHeight:e.tagLineHeight,whiteSpace:"nowrap",background:e.defaultBg,border:`${(0,u.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusSM,opacity:1,transition:`all ${e.motionDurationMid}`,textAlign:"start",position:"relative",[`&${s}-rtl`]:{direction:"rtl"},"&, a, a:hover":{color:e.defaultColor},[`${s}-close-icon`]:{marginInlineStart:a,fontSize:e.tagIconSize,color:e.colorIcon,cursor:"pointer",transition:`all ${e.motionDurationMid}`,"&:hover":{color:e.colorTextHeading}},[`&${s}-has-color`]:{borderColor:"transparent",[`&, a, a:hover, ${e.iconCls}-close, ${e.iconCls}-close:hover`]:{color:e.colorTextLightSolid}},"&-checkable":{backgroundColor:"transparent",borderColor:"transparent",cursor:"pointer",[`&:not(${s}-checkable-checked):hover`]:{color:e.colorPrimary,backgroundColor:e.colorFillSecondary},"&:active, &-checked":{color:e.colorTextLightSolid},"&-checked":{backgroundColor:e.colorPrimary,"&:hover":{backgroundColor:e.colorPrimaryHover}},"&:active":{backgroundColor:e.colorPrimaryActive}},"&-hidden":{display:"none"},[`> ${e.iconCls} + span, > span + ${e.iconCls}`]:{marginInlineStart:i}}),[`${s}-borderless`]:{borderColor:"transparent",background:e.tagBorderlessBg}}})(f(e)),m);var y=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var s=0,n=Object.getOwnPropertySymbols(e);st.indexOf(n[s])&&Object.prototype.propertyIsEnumerable.call(e,n[s])&&(r[n[s]]=e[n[s]]);return r};let b=t.forwardRef((e,n)=>{let{prefixCls:s,style:o,className:i,checked:a,children:u,icon:c,onChange:d,onClick:h}=e,p=y(e,["prefixCls","style","className","checked","children","icon","onChange","onClick"]),{getPrefixCls:f,tag:m}=t.useContext(l.ConfigContext),b=f("tag",s),[v,C,R]=g(b),S=(0,r.default)(b,`${b}-checkable`,{[`${b}-checkable-checked`]:a},null==m?void 0:m.className,i,C,R);return v(t.createElement("span",Object.assign({},p,{ref:n,style:Object.assign(Object.assign({},o),null==m?void 0:m.style),className:S,onClick:e=>{null==d||d(!a),null==h||h(e)}}),c,t.createElement("span",null,u)))});var v=e.i(403541);let C=(0,h.genSubStyleComponent)(["Tag","preset"],e=>{let t;return t=f(e),(0,v.genPresetColor)(t,(e,{textColor:r,lightBorderColor:n,lightColor:s,darkColor:o})=>({[`${t.componentCls}${t.componentCls}-${e}`]:{color:r,background:s,borderColor:n,"&-inverse":{color:t.colorTextLightSolid,background:o,borderColor:o},[`&${t.componentCls}-borderless`]:{borderColor:"transparent"}}}))},m),R=(e,t,r)=>{let n="string"!=typeof r?r:r.charAt(0).toUpperCase()+r.slice(1);return{[`${e.componentCls}${e.componentCls}-${t}`]:{color:e[`color${r}`],background:e[`color${n}Bg`],borderColor:e[`color${n}Border`],[`&${e.componentCls}-borderless`]:{borderColor:"transparent"}}}},S=(0,h.genSubStyleComponent)(["Tag","status"],e=>{let t=f(e);return[R(t,"success","Success"),R(t,"processing","Info"),R(t,"error","Error"),R(t,"warning","Warning")]},m);var O=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var s=0,n=Object.getOwnPropertySymbols(e);st.indexOf(n[s])&&Object.prototype.propertyIsEnumerable.call(e,n[s])&&(r[n[s]]=e[n[s]]);return r};let w=t.forwardRef((e,u)=>{let{prefixCls:c,className:d,rootClassName:h,style:p,children:f,icon:m,color:y,onClose:b,bordered:v=!0,visible:R}=e,w=O(e,["prefixCls","className","rootClassName","style","children","icon","color","onClose","bordered","visible"]),{getPrefixCls:k,direction:I,tag:x}=t.useContext(l.ConfigContext),[E,Q]=t.useState(!0),T=(0,n.default)(w,["closeIcon","closable"]);t.useEffect(()=>{void 0!==R&&Q(R)},[R]);let $=(0,s.isPresetColor)(y),B=(0,s.isPresetStatusColor)(y),j=$||B,U=Object.assign(Object.assign({backgroundColor:y&&!j?y:void 0},null==x?void 0:x.style),p),P=k("tag",c),[M,L,A]=g(P),N=(0,r.default)(P,null==x?void 0:x.className,{[`${P}-${y}`]:j,[`${P}-has-color`]:y&&!j,[`${P}-hidden`]:!E,[`${P}-rtl`]:"rtl"===I,[`${P}-borderless`]:!v},d,h,L,A),F=e=>{e.stopPropagation(),null==b||b(e),e.defaultPrevented||Q(!1)},[,_]=(0,o.useClosable)((0,o.pickClosable)(e),(0,o.pickClosable)(x),{closable:!1,closeIconRender:e=>{let n=t.createElement("span",{className:`${P}-close-icon`,onClick:F},e);return(0,i.replaceElement)(e,n,e=>({onClick:t=>{var r;null==(r=null==e?void 0:e.onClick)||r.call(e,t),F(t)},className:(0,r.default)(null==e?void 0:e.className,`${P}-close-icon`)}))}}),z="function"==typeof w.onClick||f&&"a"===f.type,W=m||null,D=W?t.createElement(t.Fragment,null,W,f&&t.createElement("span",null,f)):f,G=t.createElement("span",Object.assign({},T,{ref:u,className:N,style:U}),D,_,$&&t.createElement(C,{key:"preset",prefixCls:P}),B&&t.createElement(S,{key:"status",prefixCls:P}));return M(z?t.createElement(a.default,{component:"Tag"},G):G)});w.CheckableTag=b,e.s(["Tag",0,w],262218)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-px9-g~2oyp5.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-px9-g~2oyp5.js new file mode 100644 index 00000000000..0c51d099fb1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0-px9-g~2oyp5.js @@ -0,0 +1,48 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),n=e.i(540143),l=e.i(915823),a=e.i(619273),i=class extends l.Subscribable{#e;#t=void 0;#r;#n;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#l()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,a.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,a.hashKey)(t.mutationKey)!==(0,a.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#l(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#l(),this.#a()}mutate(e,t){return this.#n=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#l(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#a(e){n.notifyManager.batch(()=>{if(this.#n&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#n.onSuccess?.(e.data,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(e.data,null,t,r,n)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#n.onError?.(e.error,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(void 0,e.error,t,r,n)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,r){let l=(0,o.useQueryClient)(r),[s]=t.useState(()=>new i(l,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let d=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(n.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),c=t.useCallback((e,t)=>{s.mutate(e,t).catch(a.noop)},[s]);if(d.error&&(0,a.shouldThrowError)(s.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),a=r.forwardRef(function(e,a){return r.createElement(l.default,(0,t.default)({},e,{ref:a,icon:n}))});e.s(["ExclamationCircleOutlined",0,a],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(529681),l=e.i(242064),a=e.i(517455),i=e.i(185793),o=e.i(721369),s=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let d=e=>{var{prefixCls:n,className:a,hoverable:i=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("card",n),u=(0,r.default)(`${c}-grid`,a,{[`${c}-grid-hoverable`]:i});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),m=e.i(246422),g=e.i(838378);let p=(0,m.genStyleHooks)("Card",e=>{let t=(0,g.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:r,cardHeadPadding:n,colorBorderSecondary:l,boxShadowTertiary:a,bodyPadding:i,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:a},[`${t}-head`]:(e=>{let{antCls:t,componentCls:r,headerHeight:n,headerPadding:l,tabsMarginBottom:a}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:n,marginBottom:-1,padding:`0 ${(0,c.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${r}-typography, + > ${r}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:a,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:i,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:r,cardShadow:n,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(l)} 0 0 0 ${r}, + 0 ${(0,c.unit)(l)} 0 0 ${r}, + ${(0,c.unit)(l)} ${(0,c.unit)(l)} 0 0 ${r}, + ${(0,c.unit)(l)} 0 0 0 ${r} inset, + 0 ${(0,c.unit)(l)} 0 0 ${r} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:n}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:r,actionsLiMargin:n,cardActionsIconSize:l,colorBorderSecondary:a,actionsBg:i}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:i,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:n,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${r}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${r}`]:{fontSize:l,lineHeight:(0,c.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:r}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:n}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:r,headerPadding:n,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(n)}`,background:r,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:r,headerPaddingSM:n,headerHeightSM:l,headerFontSizeSM:a}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,c.unit)(n)}`,fontSize:a,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:r}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,r;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(r=e.headerPadding)?r:e.paddingLG}});var b=e.i(792812),h=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let f=e=>{let{actionClasses:r,actions:n=[],actionStyle:l}=e;return t.createElement("ul",{className:r,style:l},n.map((e,r)=>{let l=`action-${r}`;return t.createElement("li",{style:{width:`${100/n.length}%`},key:l},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:m,rootClassName:g,style:y,extra:x,headStyle:v={},bodyStyle:j={},title:C,loading:O,bordered:S,variant:$,size:w,type:E,cover:k,actions:T,tabList:P,children:N,activeTabKey:I,defaultActiveTabKey:M,tabBarExtraContent:B,hoverable:R,tabProps:A={},classNames:F,styles:D}=e,L=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:z,direction:H,card:_}=t.useContext(l.ConfigContext),[G]=(0,b.default)("card",$,S),W=e=>{var t;return(0,r.default)(null==(t=null==_?void 0:_.classNames)?void 0:t[e],null==F?void 0:F[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==_?void 0:_.styles)?void 0:t[e]),null==D?void 0:D[e])},X=t.useMemo(()=>{let e=!1;return t.Children.forEach(N,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[N]),q=z("card",u),[U,Q,V]=p(q),Y=t.createElement(i.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},N),Z=void 0!==I,J=Object.assign(Object.assign({},A),{[Z?"activeKey":"defaultActiveKey"]:Z?I:M,tabBarExtraContent:B}),ee=(0,a.default)(w),et=ee&&"default"!==ee?ee:"large",er=P?t.createElement(o.default,Object.assign({size:et},J,{className:`${q}-head-tabs`,onChange:t=>{var r;null==(r=e.onTabChange)||r.call(e,t)},items:P.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(C||x||er){let e=(0,r.default)(`${q}-head`,W("header")),n=(0,r.default)(`${q}-head-title`,W("title")),l=(0,r.default)(`${q}-extra`,W("extra")),a=Object.assign(Object.assign({},v),K("header"));c=t.createElement("div",{className:e,style:a},t.createElement("div",{className:`${q}-head-wrapper`},C&&t.createElement("div",{className:n,style:K("title")},C),x&&t.createElement("div",{className:l,style:K("extra")},x)),er)}let en=(0,r.default)(`${q}-cover`,W("cover")),el=k?t.createElement("div",{className:en,style:K("cover")},k):null,ea=(0,r.default)(`${q}-body`,W("body")),ei=Object.assign(Object.assign({},j),K("body")),eo=t.createElement("div",{className:ea,style:ei},O?Y:N),es=(0,r.default)(`${q}-actions`,W("actions")),ed=(null==T?void 0:T.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:T}):null,ec=(0,n.default)(L,["onTabChange"]),eu=(0,r.default)(q,null==_?void 0:_.className,{[`${q}-loading`]:O,[`${q}-bordered`]:"borderless"!==G,[`${q}-hoverable`]:R,[`${q}-contain-grid`]:X,[`${q}-contain-tabs`]:null==P?void 0:P.length,[`${q}-${ee}`]:ee,[`${q}-type-${E}`]:!!E,[`${q}-rtl`]:"rtl"===H},m,g,Q,V),em=Object.assign(Object.assign({},null==_?void 0:_.style),y);return U(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:em}),c,el,eo,ed))});var x=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};y.Grid=d,y.Meta=e=>{let{prefixCls:n,className:a,avatar:i,title:o,description:s}=e,d=x(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(l.ConfigContext),u=c("card",n),m=(0,r.default)(`${u}-meta`,a),g=i?t.createElement("div",{className:`${u}-meta-avatar`},i):null,p=o?t.createElement("div",{className:`${u}-meta-title`},o):null,b=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=p||b?t.createElement("div",{className:`${u}-meta-detail`},p,b):null;return t.createElement("div",Object.assign({},d,{className:m}),g,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(908206),l=e.i(242064),a=e.i(517455),i=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r},u=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let m=e=>{let{itemPrefixCls:n,component:l,span:a,className:i,style:o,labelStyle:d,contentStyle:c,bordered:u,label:m,content:g,colon:p,type:b,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},d),null==h?void 0:h.label),x=Object.assign(Object.assign({},c),null==h?void 0:h.content);if(u)return t.createElement(l,{colSpan:a,style:o,className:(0,r.default)(i,{[`${n}-item-${b}`]:"label"===b||"content"===b,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===b,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===b})},null!=m&&t.createElement("span",{style:y},m),null!=g&&t.createElement("span",{style:x},g));return t.createElement(l,{colSpan:a,style:o,className:(0,r.default)(`${n}-item`,i)},t.createElement("div",{className:`${n}-item-container`},null!=m&&t.createElement("span",{style:y,className:(0,r.default)(`${n}-item-label`,null==f?void 0:f.label,{[`${n}-item-no-colon`]:!p})},m),null!=g&&t.createElement("span",{style:x,className:(0,r.default)(`${n}-item-content`,null==f?void 0:f.content)},g)))};function g(e,{colon:r,prefixCls:n,bordered:l},{component:a,type:i,showLabel:o,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:g,prefixCls:p=n,className:b,style:h,labelStyle:f,contentStyle:y,span:x=1,key:v,styles:j},C)=>"string"==typeof a?t.createElement(m,{key:`${i}-${v||C}`,className:b,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),null==j?void 0:j.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),y),null==j?void 0:j.content)},span:x,colon:r,component:a,itemPrefixCls:p,bordered:l,label:o?e:null,content:s?g:null,type:i}):[t.createElement(m,{key:`label-${v||C}`,className:b,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),f),null==j?void 0:j.label),span:1,colon:r,component:a[0],itemPrefixCls:p,bordered:l,label:e,type:"label"}),t.createElement(m,{key:`content-${v||C}`,className:b,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),h),y),null==j?void 0:j.content),span:2*x-1,component:a[1],itemPrefixCls:p,bordered:l,content:g,type:"content"})])}let p=e=>{let r=t.useContext(s),{prefixCls:n,vertical:l,row:a,index:i,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${i}`,className:`${n}-row`},g(a,e,Object.assign({component:"th",type:"label",showLabel:!0},r))),t.createElement("tr",{key:`content-${i}`,className:`${n}-row`},g(a,e,Object.assign({component:"td",type:"content",showContent:!0},r)))):t.createElement("tr",{key:i,className:`${n}-row`},g(a,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},r)))};e.i(296059);var b=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let x=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:r,itemPaddingBottom:n,itemPaddingEnd:l,colonMarginRight:a,colonMarginLeft:i,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:r}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.padding)} ${(0,b.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:r,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.paddingSM)} ${(0,b.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.paddingXS)} ${(0,b.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:r,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:n,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,b.unit)(i)} ${(0,b.unit)(a)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var v=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let j=e=>{let m,{prefixCls:g,title:b,extra:h,column:f,colon:y=!0,bordered:j,layout:C,children:O,className:S,rootClassName:$,style:w,size:E,labelStyle:k,contentStyle:T,styles:P,items:N,classNames:I}=e,M=v(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:B,direction:R,className:A,style:F,classNames:D,styles:L}=(0,l.useComponentConfig)("descriptions"),z=B("descriptions",g),H=(0,i.default)(),_=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,n.matchScreen)(H,Object.assign(Object.assign({},o),f)))?e:3},[H,f]),G=(m=t.useMemo(()=>N||(0,d.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[N,O]),t.useMemo(()=>m.map(e=>{var{span:t}=e,r=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},r),{filled:!0}):Object.assign(Object.assign({},r),{span:"number"==typeof t?t:(0,n.matchScreen)(H,t)})}),[m,H])),W=(0,a.default)(E),K=((e,r)=>{let[n,l]=(0,t.useMemo)(()=>{let t,n,l,a;return t=[],n=[],l=!1,a=0,r.filter(e=>e).forEach(r=>{let{filled:i}=r,o=u(r,["filled"]);if(i){n.push(o),t.push(n),n=[],a=0;return}let s=e-a;(a+=r.span||1)>=e?(a>e?(l=!0,n.push(Object.assign(Object.assign({},o),{span:s}))):n.push(o),t.push(n),n=[],a=0):n.push(o)}),n.length>0&&t.push(n),[t=t.map(t=>{let r=t.reduce((e,t)=>e+(t.span||1),0);if(r({labelStyle:k,contentStyle:T,styles:{content:Object.assign(Object.assign({},L.content),null==P?void 0:P.content),label:Object.assign(Object.assign({},L.label),null==P?void 0:P.label)},classNames:{label:(0,r.default)(D.label,null==I?void 0:I.label),content:(0,r.default)(D.content,null==I?void 0:I.content)}}),[k,T,P,I,D,L]);return X(t.createElement(s.Provider,{value:Q},t.createElement("div",Object.assign({className:(0,r.default)(z,A,D.root,null==I?void 0:I.root,{[`${z}-${W}`]:W&&"default"!==W,[`${z}-bordered`]:!!j,[`${z}-rtl`]:"rtl"===R},S,$,q,U),style:Object.assign(Object.assign(Object.assign(Object.assign({},F),L.root),null==P?void 0:P.root),w)},M),(b||h)&&t.createElement("div",{className:(0,r.default)(`${z}-header`,D.header,null==I?void 0:I.header),style:Object.assign(Object.assign({},L.header),null==P?void 0:P.header)},b&&t.createElement("div",{className:(0,r.default)(`${z}-title`,D.title,null==I?void 0:I.title),style:Object.assign(Object.assign({},L.title),null==P?void 0:P.title)},b),h&&t.createElement("div",{className:(0,r.default)(`${z}-extra`,D.extra,null==I?void 0:I.extra),style:Object.assign(Object.assign({},L.extra),null==P?void 0:P.extra)},h)),t.createElement("div",{className:`${z}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,r)=>t.createElement(p,{key:r,index:r,colon:y,prefixCls:z,vertical:"vertical"===C,bordered:j,row:e}))))))))};j.Item=({children:e})=>e,e.s(["Descriptions",0,j],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),r=e.i(732961),n=e.i(289882),l=e.i(170517),a=e.i(628882),i=e.i(320890),o=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),m=e.i(328052),g=e.i(135551);let p=(e,t)=>new g.FastColor(e).setA(t).toRgbString(),b=(e,t)=>new g.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let r=e||"#000",n=t||"#fff";return{colorBgBase:r,colorTextBase:n,colorText:p(n,.85),colorTextSecondary:p(n,.65),colorTextTertiary:p(n,.45),colorTextQuaternary:p(n,.25),colorFill:p(n,.18),colorFillSecondary:p(n,.12),colorFillTertiary:p(n,.08),colorFillQuaternary:p(n,.04),colorBgSolid:p(n,.95),colorBgSolidHover:p(n,1),colorBgSolidActive:p(n,.9),colorBgElevated:b(r,12),colorBgContainer:b(r,8),colorBgLayout:b(r,0),colorBgSpotlight:b(r,26),colorBgBlur:p(n,.04),colorBorder:b(r,26),colorBorderSecondary:b(r,19)}},y={defaultSeed:i.defaultConfig.token,useToken:function(){let[e,t,r]=(0,o.useToken)();return{theme:e,token:t,hashId:r}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let r=Object.keys(l.defaultPresetColors).map(t=>{let r=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,n,l)=>(e[`${t}-${l+1}`]=r[l],e[`${t}${l+1}`]=r[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),n=null!=t?t:(0,s.default)(e),a=(0,m.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},n),r),a),{colorPrimaryBg:a.colorPrimaryBorder,colorPrimaryBgHover:a.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let r=null!=t?t:(0,s.default)(e),n=r.fontSizeSM,l=r.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},r),function(e){let{sizeUnit:t,sizeStep:r}=e,n=r-2;return{sizeXXL:t*(n+10),sizeXL:t*(n+6),sizeLG:t*(n+2),sizeMD:t*(n+2),sizeMS:t*(n+1),size:t*n,sizeSM:t*n,sizeXS:t*(n-1),sizeXXS:t*(n-1)}}(null!=t?t:e)),(0,c.default)(n)),{controlHeight:l}),(0,d.default)(Object.assign(Object.assign({},r),{controlHeight:l})))},getDesignToken:e=>{let i=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):n.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,r.getComputedToken)(o,{override:null==e?void 0:e.token},i,a.default)},defaultConfig:i.defaultConfig,_internalContext:i.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),r=e.i(560445),n=e.i(175712),l=e.i(869216),a=e.i(311451),i=e.i(212931),o=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:m,message:g,resourceInformationTitle:p,resourceInformation:b,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:x}){let{Title:v,Text:j}=o.Typography,{token:C}=s.theme.useToken(),[O,S]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&S("")},[e]),(0,t.jsx)(i.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!x&&O!==x||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[m&&(0,t.jsx)(r.Alert,{message:m,type:"warning"}),(0,t.jsx)(n.Card,{title:p,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder}},style:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:b&&b.map(({label:e,value:r,...n})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(j,{...n,children:r??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(j,{children:g})}),x&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(j,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(j,{children:"Type "}),(0,t.jsx)(j,{strong:!0,type:"danger",children:x}),(0,t.jsx)(j,{children:" to confirm deletion:"})]}),(0,t.jsx)(a.Input,{value:O,onChange:e=>S(e.target.value),placeholder:x,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:C.colorError}}),autoFocus:!0})]})]})})}])},233538,e=>{"use strict";e.s(["isDisabledReactIssue7711",0,function(e){let t=e.parentElement,r=null;for(;t&&!(t instanceof HTMLFieldSetElement);)t instanceof HTMLLegendElement&&(r=t),t=t.parentElement;let n=(null==t?void 0:t.getAttribute("disabled"))==="";return!(n&&function(e){if(!e)return!1;let t=e.previousElementSibling;for(;null!==t;){if(t instanceof HTMLLegendElement)return!1;t=t.previousElementSibling}return!0}(r))&&n}])},83733,233137,e=>{"use strict";let t,r;var n,l,a=e.i(247167),i=e.i(271645),o=e.i(544508),s=e.i(746725),d=e.i(835696);void 0!==a.default&&"u">typeof globalThis&&"u">typeof Element&&(null==(n=null==a.default?void 0:a.default.env)?void 0:n.NODE_ENV)==="test"&&void 0===(null==(l=null==Element?void 0:Element.prototype)?void 0:l.getAnimations)&&(Element.prototype.getAnimations=function(){return console.warn(["Headless UI has polyfilled `Element.prototype.getAnimations` for your tests.","Please install a proper polyfill e.g. `jsdom-testing-mocks`, to silence these warnings.","","Example usage:","```js","import { mockAnimationsApi } from 'jsdom-testing-mocks'","mockAnimationsApi()","```"].join(` +`)),[]});var c=((t=c||{})[t.None=0]="None",t[t.Closed=1]="Closed",t[t.Enter=2]="Enter",t[t.Leave=4]="Leave",t);e.s(["transitionDataAttributes",0,function(e){let t={};for(let r in e)!0===e[r]&&(t[`data-${r}`]="");return t},"useTransition",0,function(e,t,r,n){let[l,a]=(0,i.useState)(r),{hasFlag:c,addFlag:u,removeFlag:m}=function(e=0){let[t,r]=(0,i.useState)(e),n=(0,i.useCallback)(e=>r(e),[t]),l=(0,i.useCallback)(e=>r(t=>t|e),[t]),a=(0,i.useCallback)(e=>(t&e)===e,[t]);return{flags:t,setFlag:n,addFlag:l,hasFlag:a,removeFlag:(0,i.useCallback)(e=>r(t=>t&~e),[r]),toggleFlag:(0,i.useCallback)(e=>r(t=>t^e),[r])}}(e&&l?3:0),g=(0,i.useRef)(!1),p=(0,i.useRef)(!1),b=(0,s.useDisposables)();return(0,d.useIsoMorphicEffect)(()=>{var l;if(e){if(r&&a(!0),!t){r&&u(3);return}return null==(l=null==n?void 0:n.start)||l.call(n,r),function(e,{prepare:t,run:r,done:n,inFlight:l}){let a=(0,o.disposables)();return function(e,{inFlight:t,prepare:r}){if(null!=t&&t.current)return r();let n=e.style.transition;e.style.transition="none",r(),e.offsetHeight,e.style.transition=n}(e,{prepare:t,inFlight:l}),a.nextFrame(()=>{r(),a.requestAnimationFrame(()=>{a.add(function(e,t){var r,n;let l=(0,o.disposables)();if(!e)return l.dispose;let a=!1;l.add(()=>{a=!0});let i=null!=(n=null==(r=e.getAnimations)?void 0:r.call(e).filter(e=>e instanceof CSSTransition))?n:[];return 0===i.length?t():Promise.allSettled(i.map(e=>e.finished)).then(()=>{a||t()}),l.dispose}(e,n))})}),a.dispose}(t,{inFlight:g,prepare(){p.current?p.current=!1:p.current=g.current,g.current=!0,p.current||(r?(u(3),m(4)):(u(4),m(2)))},run(){p.current?r?(m(3),u(4)):(m(4),u(3)):r?m(1):u(1)},done(){var e;p.current&&"function"==typeof t.getAnimations&&t.getAnimations().length>0||(g.current=!1,m(7),r||a(!1),null==(e=null==n?void 0:n.end)||e.call(n,r))}})}},[e,r,t,b]),e?[l,{closed:c(1),enter:c(2),leave:c(4),transition:c(2)||c(4)}]:[r,{closed:void 0,enter:void 0,leave:void 0,transition:void 0}]}],83733);let u=(0,i.createContext)(null);u.displayName="OpenClosedContext";var m=((r=m||{})[r.Open=1]="Open",r[r.Closed=2]="Closed",r[r.Closing=4]="Closing",r[r.Opening=8]="Opening",r);e.s(["OpenClosedProvider",0,function({value:e,children:t}){return i.default.createElement(u.Provider,{value:e},t)},"ResetOpenClosedProvider",0,function({children:e}){return i.default.createElement(u.Provider,{value:null},e)},"State",0,m,"useOpenClosed",0,function(){return(0,i.useContext)(u)}],233137)},677667,674175,886148,543086,e=>{"use strict";let t,r;var n,l=e.i(290571),a=e.i(783222),i=e.i(433336),o=e.i(271645),s=e.i(394487),d=e.i(914189),c=e.i(144279),u=e.i(294316),m=e.i(83733);let g=(0,o.createContext)(()=>{});function p({value:e,children:t}){return o.default.createElement(g.Provider,{value:e},t)}e.s(["CloseProvider",0,p],674175);var b=e.i(233137),h=e.i(233538),f=e.i(397701),y=e.i(402155),x=e.i(700020);let v=null!=(n=o.default.startTransition)?n:function(e){e()};var j=e.i(998348),C=((t=C||{})[t.Open=0]="Open",t[t.Closed=1]="Closed",t),O=((r=O||{})[r.ToggleDisclosure=0]="ToggleDisclosure",r[r.CloseDisclosure=1]="CloseDisclosure",r[r.SetButtonId=2]="SetButtonId",r[r.SetPanelId=3]="SetPanelId",r[r.SetButtonElement=4]="SetButtonElement",r[r.SetPanelElement=5]="SetPanelElement",r);let S={0:e=>({...e,disclosureState:(0,f.match)(e.disclosureState,{0:1,1:0})}),1:e=>1===e.disclosureState?e:{...e,disclosureState:1},2:(e,t)=>e.buttonId===t.buttonId?e:{...e,buttonId:t.buttonId},3:(e,t)=>e.panelId===t.panelId?e:{...e,panelId:t.panelId},4:(e,t)=>e.buttonElement===t.element?e:{...e,buttonElement:t.element},5:(e,t)=>e.panelElement===t.element?e:{...e,panelElement:t.element}},$=(0,o.createContext)(null);function w(e){let t=(0,o.useContext)($);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,w),t}return t}$.displayName="DisclosureContext";let E=(0,o.createContext)(null);E.displayName="DisclosureAPIContext";let k=(0,o.createContext)(null);function T(e,t){return(0,f.match)(t.type,S,e,t)}k.displayName="DisclosurePanelContext";let P=o.Fragment,N=x.RenderFeatures.RenderStrategy|x.RenderFeatures.Static,I=Object.assign((0,x.forwardRefWithAs)(function(e,t){let{defaultOpen:r=!1,...n}=e,l=(0,o.useRef)(null),a=(0,u.useSyncRefs)(t,(0,u.optionalRef)(e=>{l.current=e},void 0===e.as||e.as===o.Fragment)),i=(0,o.useReducer)(T,{disclosureState:+!r,buttonElement:null,panelElement:null,buttonId:null,panelId:null}),[{disclosureState:s,buttonId:c},m]=i,g=(0,d.useEvent)(e=>{m({type:1});let t=(0,y.getOwnerDocument)(l);if(!t||!c)return;let r=e?e instanceof HTMLElement?e:e.current instanceof HTMLElement?e.current:t.getElementById(c):t.getElementById(c);null==r||r.focus()}),h=(0,o.useMemo)(()=>({close:g}),[g]),v=(0,o.useMemo)(()=>({open:0===s,close:g}),[s,g]),j=(0,x.useRender)();return o.default.createElement($.Provider,{value:i},o.default.createElement(E.Provider,{value:h},o.default.createElement(p,{value:g},o.default.createElement(b.OpenClosedProvider,{value:(0,f.match)(s,{0:b.State.Open,1:b.State.Closed})},j({ourProps:{ref:a},theirProps:n,slot:v,defaultTag:P,name:"Disclosure"})))))}),{Button:(0,x.forwardRefWithAs)(function(e,t){let r=(0,o.useId)(),{id:n=`headlessui-disclosure-button-${r}`,disabled:l=!1,autoFocus:m=!1,...g}=e,[p,b]=w("Disclosure.Button"),f=(0,o.useContext)(k),y=null!==f&&f===p.panelId,v=(0,o.useRef)(null),C=(0,u.useSyncRefs)(v,t,(0,d.useEvent)(e=>{if(!y)return b({type:4,element:e})}));(0,o.useEffect)(()=>{if(!y)return b({type:2,buttonId:n}),()=>{b({type:2,buttonId:null})}},[n,b,y]);let O=(0,d.useEvent)(e=>{var t;if(y){if(1===p.disclosureState)return;switch(e.key){case j.Keys.Space:case j.Keys.Enter:e.preventDefault(),e.stopPropagation(),b({type:0}),null==(t=p.buttonElement)||t.focus()}}else switch(e.key){case j.Keys.Space:case j.Keys.Enter:e.preventDefault(),e.stopPropagation(),b({type:0})}}),S=(0,d.useEvent)(e=>{e.key===j.Keys.Space&&e.preventDefault()}),$=(0,d.useEvent)(e=>{var t;(0,h.isDisabledReactIssue7711)(e.currentTarget)||l||(y?(b({type:0}),null==(t=p.buttonElement)||t.focus()):b({type:0}))}),{isFocusVisible:E,focusProps:T}=(0,a.useFocusRing)({autoFocus:m}),{isHovered:P,hoverProps:N}=(0,i.useHover)({isDisabled:l}),{pressed:I,pressProps:M}=(0,s.useActivePress)({disabled:l}),B=(0,o.useMemo)(()=>({open:0===p.disclosureState,hover:P,active:I,disabled:l,focus:E,autofocus:m}),[p,P,I,E,l,m]),R=(0,c.useResolveButtonType)(e,p.buttonElement),A=y?(0,x.mergeProps)({ref:C,type:R,disabled:l||void 0,autoFocus:m,onKeyDown:O,onClick:$},T,N,M):(0,x.mergeProps)({ref:C,id:n,type:R,"aria-expanded":0===p.disclosureState,"aria-controls":p.panelElement?p.panelId:void 0,disabled:l||void 0,autoFocus:m,onKeyDown:O,onKeyUp:S,onClick:$},T,N,M);return(0,x.useRender)()({ourProps:A,theirProps:g,slot:B,defaultTag:"button",name:"Disclosure.Button"})}),Panel:(0,x.forwardRefWithAs)(function(e,t){let r=(0,o.useId)(),{id:n=`headlessui-disclosure-panel-${r}`,transition:l=!1,...a}=e,[i,s]=w("Disclosure.Panel"),{close:c}=function e(t){let r=(0,o.useContext)(E);if(null===r){let r=Error(`<${t} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(r,e),r}return r}("Disclosure.Panel"),[g,p]=(0,o.useState)(null),h=(0,u.useSyncRefs)(t,(0,d.useEvent)(e=>{v(()=>s({type:5,element:e}))}),p);(0,o.useEffect)(()=>(s({type:3,panelId:n}),()=>{s({type:3,panelId:null})}),[n,s]);let f=(0,b.useOpenClosed)(),[y,j]=(0,m.useTransition)(l,g,null!==f?(f&b.State.Open)===b.State.Open:0===i.disclosureState),C=(0,o.useMemo)(()=>({open:0===i.disclosureState,close:c}),[i.disclosureState,c]),O={ref:h,id:n,...(0,m.transitionDataAttributes)(j)},S=(0,x.useRender)();return o.default.createElement(b.ResetOpenClosedProvider,null,o.default.createElement(k.Provider,{value:i.panelId},S({ourProps:O,theirProps:a,slot:C,defaultTag:"div",features:N,visible:y,name:"Disclosure.Panel"})))})});e.s(["Disclosure",0,I],886148);let M=(0,o.createContext)(void 0);var B=e.i(444755);let R=(0,e.i(673706).makeClassName)("Accordion"),A=(0,o.createContext)({isOpen:!1}),F=o.default.forwardRef((e,t)=>{var r;let{defaultOpen:n=!1,children:a,className:i}=e,s=(0,l.__rest)(e,["defaultOpen","children","className"]),d=null!=(r=(0,o.useContext)(M))?r:(0,B.tremorTwMerge)("rounded-tremor-default border");return o.default.createElement(I,Object.assign({as:"div",ref:t,className:(0,B.tremorTwMerge)(R("root"),"overflow-hidden","bg-tremor-background border-tremor-border","dark:bg-dark-tremor-background dark:border-dark-tremor-border",d,i),defaultOpen:n},s),({open:e})=>o.default.createElement(A.Provider,{value:{isOpen:e}},a))});F.displayName="Accordion",e.s(["OpenContext",0,A,"default",0,F],543086),e.s(["Accordion",0,F],677667)},898667,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(886148);let l=e=>{var n=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},n),r.default.createElement("path",{d:"M11.9999 10.8284L7.0502 15.7782L5.63599 14.364L11.9999 8L18.3639 14.364L16.9497 15.7782L11.9999 10.8284Z"}))};var a=e.i(543086),i=e.i(444755);let o=(0,e.i(673706).makeClassName)("AccordionHeader"),s=r.default.forwardRef((e,s)=>{let{children:d,className:c}=e,u=(0,t.__rest)(e,["children","className"]),{isOpen:m}=(0,r.useContext)(a.OpenContext);return r.default.createElement(n.Disclosure.Button,Object.assign({ref:s,className:(0,i.tremorTwMerge)(o("root"),"w-full flex items-center justify-between px-4 py-3","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis",c)},u),r.default.createElement("div",{className:(0,i.tremorTwMerge)(o("children"),"flex flex-1 text-inherit mr-4")},d),r.default.createElement("div",null,r.default.createElement(l,{className:(0,i.tremorTwMerge)(o("arrowIcon"),"h-5 w-5 -mr-1","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle",m?"transition-all":"transition-all -rotate-180")})))});s.displayName="AccordionHeader",e.s(["AccordionHeader",0,s],898667)},130643,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(886148),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("AccordionBody"),i=r.default.forwardRef((e,i)=>{let{children:o,className:s}=e,d=(0,t.__rest)(e,["children","className"]);return r.default.createElement(n.Disclosure.Panel,Object.assign({ref:i,className:(0,l.tremorTwMerge)(a("root"),"w-full text-tremor-default px-4 pb-3","text-tremor-content","dark:text-dark-tremor-content",s)},d),o)});i.displayName="AccordionBody",e.s(["AccordionBody",0,i],130643)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(829087),l=e.i(480731),a=e.i(444755),i=e.i(673706),o=e.i(95779);let s={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,i.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:p="simple",tooltip:b,size:h=l.Sizes.SM,color:f,className:y}=e,x=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),v=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,i.getColorClassNames)(t,o.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,f),{tooltipProps:j,getReferenceProps:C}=(0,n.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,a.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",v.bgColor,v.textColor,v.borderColor,v.ringColor,c[p].rounded,c[p].border,c[p].shadow,c[p].ring,s[h].paddingX,s[h].paddingY,y)},C,x),r.default.createElement(n.default,Object.assign({text:b},j)),r.default.createElement(g,{className:(0,a.tremorTwMerge)(u("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),n=e.i(122577),l=e.i(278587),a=e.i(68155),i=e.i(360820),o=e.i(871943),s=e.i(434626),d=e.i(551332),c=e.i(592968),u=e.i(115504),m=e.i(752978);function g({icon:e,onClick:r,className:n,disabled:l,dataTestId:a}){return l?(0,t.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,t.jsx)(m.Icon,{icon:e,size:"sm",onClick:r,className:(0,u.cx)("cursor-pointer",n),"data-testid":a})}let p={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:n.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:l.RefreshIcon,className:"hover:text-green-600"},Up:{icon:i.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:o.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:s.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:n=!1,disabledTooltipText:l,dataTestId:a,variant:i}){let{icon:o,className:s}=p[i];return(0,t.jsx)(c.Tooltip,{title:n?l:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(g,{icon:o,onClick:e,className:s,disabled:n,dataTestId:a})})})}],902555)},359200,e=>{"use strict";var t=e.i(843476),r=e.i(994388),n=e.i(304967),l=e.i(197647),a=e.i(653824),i=e.i(269200),o=e.i(942232),s=e.i(977572),d=e.i(427612),c=e.i(64848),u=e.i(496020),m=e.i(881073),g=e.i(404206),p=e.i(723731),b=e.i(599724),h=e.i(271645),f=e.i(650056),y=e.i(127952),x=e.i(902555),v=e.i(727749),j=e.i(266027),C=e.i(954616),O=e.i(912598),S=e.i(243652),$=e.i(602869),w=e.i(135214);let E=(0,S.createQueryKeys)("budgets");e.i(622826);var k=e.i(964471),T=e.i(779241),P=e.i(677667),N=e.i(898667),I=e.i(130643),M=e.i(464571),B=e.i(212931),R=e.i(808613),A=e.i(28651),F=e.i(199133);let D=({isModalVisible:e,setIsModalVisible:r})=>{let[n]=R.Form.useForm(),l=(()=>{let{accessToken:e}=(0,w.default)(),t=(0,O.useQueryClient)();return(0,C.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return(0,$.budgetCreateCall)(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:E.all})}})})(),a=async e=>{try{v.default.info("Making API Call"),await l.mutateAsync(e),v.default.success("Budget Created"),n.resetFields(),r(!1)}catch(e){console.error("Error creating the budget:",e),v.default.fromBackend(`Error creating the budget: ${e}`)}};return(0,t.jsx)(B.Modal,{title:"Create Budget",open:e,width:800,footer:null,onOk:()=>{r(!1),n.resetFields()},onCancel:()=>{r(!1),n.resetFields()},children:(0,t.jsxs)(R.Form,{form:n,onFinish:a,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(R.Form.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,t.jsx)(T.TextInput,{placeholder:""})}),(0,t.jsx)(R.Form.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsxs)(P.Accordion,{className:"mt-20 mb-8",children:[(0,t.jsx)(N.AccordionHeader,{children:(0,t.jsx)("b",{children:"Optional Settings"})}),(0,t.jsxs)(I.AccordionBody,{children:[(0,t.jsx)(R.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(A.InputNumber,{step:.01,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(F.Select,{defaultValue:null,placeholder:"n/a",children:[(0,t.jsx)(F.Select.Option,{value:"24h",children:"daily"}),(0,t.jsx)(F.Select.Option,{value:"7d",children:"weekly"}),(0,t.jsx)(F.Select.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(M.Button,{htmlType:"submit",children:"Create Budget"})})]})})},L=({isModalVisible:e,setIsModalVisible:r,existingBudget:n})=>{let[l]=R.Form.useForm(),a=(()=>{let{accessToken:e}=(0,w.default)(),t=(0,O.useQueryClient)();return(0,C.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return(0,$.budgetUpdateCall)(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:E.all})}})})();(0,h.useEffect)(()=>{l.setFieldsValue(n)},[n,l]);let i=async e=>{try{v.default.info("Making API Call"),await a.mutateAsync(e),v.default.success("Budget Updated"),l.resetFields(),r(!1)}catch(e){console.error("Error updating the budget:",e),v.default.fromBackend(`Error updating the budget: ${e}`)}};return(0,t.jsx)(B.Modal,{title:"Edit Budget",open:e,width:800,footer:null,onOk:()=>{r(!1),l.resetFields()},onCancel:()=>{r(!1),l.resetFields()},children:(0,t.jsxs)(R.Form,{form:l,onFinish:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:n,children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(R.Form.Item,{label:"Budget ID",name:"budget_id",help:"Budget ID cannot be changed after creation",children:(0,t.jsx)(T.TextInput,{placeholder:"",disabled:!0})}),(0,t.jsx)(R.Form.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsxs)(P.Accordion,{className:"mt-20 mb-8",children:[(0,t.jsx)(N.AccordionHeader,{children:(0,t.jsx)("b",{children:"Optional Settings"})}),(0,t.jsxs)(I.AccordionBody,{children:[(0,t.jsx)(R.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(A.InputNumber,{step:.01,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(F.Select,{defaultValue:null,placeholder:"n/a",children:[(0,t.jsx)(F.Select.Option,{value:"24h",children:"daily"}),(0,t.jsx)(F.Select.Option,{value:"7d",children:"weekly"}),(0,t.jsx)(F.Select.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(M.Button,{htmlType:"submit",children:"Save"})})]})})},z=` +curl -X POST --location '/end_user/new' \\ + +-H 'Authorization: Bearer ' \\ + +-H 'Content-Type: application/json' \\ + +-d '{"user_id": "my-customer-id', "budget_id": ""}' # 👈 KEY CHANGE + +`,H=` +curl -X POST --location '/chat/completions' \\ + +-H 'Authorization: Bearer ' \\ + +-H 'Content-Type: application/json' \\ + +-d '{ + "model": "gpt-3.5-turbo', + "messages":[{"role": "user", "content": "Hey, how's it going?"}], + "user": "my-customer-id" +}' # 👈 KEY CHANGE + +`,_=`from openai import OpenAI +client = OpenAI( + base_url="", + api_key="" +) + +completion = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello!"} + ], + user="my-customer-id" +) + +print(completion.choices[0].message)`;var G=e.i(708347);let W=({accessToken:e})=>{let[S,T]=(0,h.useState)(!1),[P,N]=(0,h.useState)(!1),[I,M]=(0,h.useState)(null),[B,R]=(0,h.useState)(!1),{userRole:A}=(0,w.default)(),F=(0,G.isProxyAdminRole)(A??""),{data:W=[]}=(()=>{let{accessToken:e}=(0,w.default)();return(0,j.useQuery)({queryKey:E.list({}),queryFn:async()=>(await (0,$.getBudgetList)(e)??[]).filter(e=>null!=e),enabled:!!e})})(),K=(()=>{let{accessToken:e}=(0,w.default)(),t=(0,O.useQueryClient)();return(0,C.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return(0,$.budgetDeleteCall)(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:E.all})}})})(),X=async t=>{null!=e&&(M(t),N(!0))},q=async()=>{if(I&&null!=e)try{await K.mutateAsync(I.budget_id),v.default.success("Budget deleted.")}catch(e){console.error("Error deleting budget:",e),"function"==typeof v.default.fromBackend?v.default.fromBackend("Failed to delete budget"):v.default.info("Failed to delete budget")}finally{R(!1),M(null)}};return(0,t.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[F&&(0,t.jsx)(r.Button,{size:"sm",variant:"primary",className:"mb-2",onClick:()=>T(!0),children:"+ Create Budget"}),(0,t.jsxs)(a.TabGroup,{children:[(0,t.jsxs)(m.TabList,{children:[(0,t.jsx)(l.Tab,{children:"Budgets"}),(0,t.jsx)(l.Tab,{children:"Examples"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(g.TabPanel,{children:(0,t.jsxs)("div",{className:"mt-6",children:[(0,t.jsx)(D,{isModalVisible:S,setIsModalVisible:T}),I&&(0,t.jsx)(L,{isModalVisible:P,setIsModalVisible:N,existingBudget:I}),(0,t.jsxs)(n.Card,{children:[(0,t.jsx)(b.Text,{children:"Create a budget to assign to customers."}),(0,t.jsxs)(i.Table,{children:[(0,t.jsx)(d.TableHead,{children:(0,t.jsxs)(u.TableRow,{children:[(0,t.jsx)(c.TableHeaderCell,{children:"Budget ID"}),(0,t.jsx)(c.TableHeaderCell,{children:"Max Budget"}),(0,t.jsx)(c.TableHeaderCell,{children:"TPM"}),(0,t.jsx)(c.TableHeaderCell,{children:"RPM"})]})}),(0,t.jsx)(o.TableBody,{children:W.slice().sort((e,t)=>new Date(t.updated_at).getTime()-new Date(e.updated_at).getTime()).map(e=>(0,t.jsxs)(u.TableRow,{children:[(0,t.jsx)(s.TableCell,{children:e.budget_id}),(0,t.jsx)(s.TableCell,{children:(0,t.jsx)(k.MoneyCell,{value:e.max_budget,decimals:2,showZero:!0,emptyText:"Unlimited"})}),(0,t.jsx)(s.TableCell,{children:e.tpm_limit?e.tpm_limit:"n/a"}),(0,t.jsx)(s.TableCell,{children:e.rpm_limit?e.rpm_limit:"n/a"}),F&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(x.default,{variant:"Edit",tooltipText:"Edit budget",onClick:()=>X(e),dataTestId:"edit-budget-button"}),(0,t.jsx)(x.default,{variant:"Delete",tooltipText:"Delete budget",onClick:()=>{M(e),R(!0)},dataTestId:"delete-budget-button"})]})]},e.budget_id))})]})]}),(0,t.jsx)(y.default,{isOpen:B,title:"Delete Budget?",message:"Are you sure you want to delete this budget? This action cannot be undone.",resourceInformationTitle:"Budget Information",resourceInformation:[{label:"Budget ID",value:I?.budget_id,code:!0},{label:"Max Budget",value:I?.max_budget},{label:"TPM",value:I?.tpm_limit},{label:"RPM",value:I?.rpm_limit}],onCancel:()=>{R(!1)},onOk:q,confirmLoading:K.isPending})]})}),(0,t.jsx)(g.TabPanel,{children:(0,t.jsxs)("div",{className:"mt-6",children:[(0,t.jsx)(b.Text,{className:"text-base",children:"How to use budget id"}),(0,t.jsxs)(a.TabGroup,{children:[(0,t.jsxs)(m.TabList,{children:[(0,t.jsx)(l.Tab,{children:"Assign Budget to Customer"}),(0,t.jsx)(l.Tab,{children:"Test it (Curl)"}),(0,t.jsx)(l.Tab,{children:"Test it (OpenAI SDK)"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(g.TabPanel,{children:(0,t.jsx)(f.Prism,{language:"bash",children:z})}),(0,t.jsx)(g.TabPanel,{children:(0,t.jsx)(f.Prism,{language:"bash",children:H})}),(0,t.jsx)(g.TabPanel,{children:(0,t.jsx)(f.Prism,{language:"python",children:_})})]})]})]})})]})]})]})};e.s(["default",0,function(){let{accessToken:e}=(0,w.default)();return(0,t.jsx)(W,{accessToken:e})}],359200)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-s2am3eulbyd.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-s2am3eulbyd.js deleted file mode 100644 index d5b9e0099b9..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0-s2am3eulbyd.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,l],250980)},309426,e=>{"use strict";var t=e.i(290571),l=e.i(444755),s=e.i(673706),r=e.i(271645),a=e.i(46757);let n=(0,s.makeClassName)("Col"),i=r.default.forwardRef((e,s)=>{let i,o,c,d,{numColSpan:u=1,numColSpanSm:m,numColSpanMd:g,numColSpanLg:p,children:h,className:x}=e,f=(0,t.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),y=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"";return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(n("root"),(i=y(u,a.colSpan),o=y(m,a.colSpanSm),c=y(g,a.colSpanMd),d=y(p,a.colSpanLg),(0,l.tremorTwMerge)(i,o,c,d)),x)},f),h)});i.displayName="Col",e.s(["Col",0,i],309426)},435451,e=>{"use strict";var t=e.i(843476),l=e.i(290571),s=e.i(271645);let r=e=>{var t=(0,l.__rest)(e,[]);return s.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),s.default.createElement("path",{d:"M12 4v16m8-8H4"}))},a=e=>{var t=(0,l.__rest)(e,[]);return s.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),s.default.createElement("path",{d:"M20 12H4"}))};var n=e.i(444755),i=e.i(673706),o=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",d="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",u=s.default.forwardRef((e,t)=>{let{onSubmit:u,enableStepper:m=!0,disabled:g,onValueChange:p,onChange:h}=e,x=(0,l.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),f=(0,s.useRef)(null),[y,b]=s.default.useState(!1),v=s.default.useCallback(()=>{b(!0)},[]),w=s.default.useCallback(()=>{b(!1)},[]),[j,N]=s.default.useState(!1),S=s.default.useCallback(()=>{N(!0)},[]),k=s.default.useCallback(()=>{N(!1)},[]);return s.default.createElement(o.default,Object.assign({type:"number",ref:(0,i.mergeRefs)([f,t]),disabled:g,makeInputClassName:(0,i.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=f.current)?void 0:t.value;null==u||u(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&v(),"ArrowUp"===e.key&&S()},onKeyUp:e=>{"ArrowDown"===e.key&&w(),"ArrowUp"===e.key&&k()},onChange:e=>{g||(null==p||p(parseFloat(e.target.value)),null==h||h(e))},stepper:m?s.default.createElement("div",{className:(0,n.tremorTwMerge)("flex justify-center align-middle")},s.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;g||(null==(e=f.current)||e.stepDown(),null==(t=f.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!g&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},s.default.createElement(a,{"data-testid":"step-down",className:(y?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),s.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;g||(null==(e=f.current)||e.stepUp(),null==(t=f.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!g&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},s.default.createElement(r,{"data-testid":"step-up",className:(j?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},x))});u.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:l={width:"100%"},placeholder:s="Enter a numerical value",min:r,max:a,onChange:n,...i})=>(0,t.jsx)(u,{onWheel:e=>e.currentTarget.blur(),step:e,style:l,placeholder:s,min:r,max:a,onChange:n,...i})],435451)},860585,e=>{"use strict";var t=e.i(843476),l=e.i(199133);let{Option:s}=l.Select;e.s(["default",0,({value:e,onChange:r,className:a="",style:n={}})=>(0,t.jsxs)(l.Select,{style:{width:"100%",...n},value:e||void 0,onChange:r,className:a,placeholder:"n/a",allowClear:!0,children:[(0,t.jsx)(s,{value:"1h",children:"hourly"}),(0,t.jsx)(s,{value:"24h",children:"daily"}),(0,t.jsx)(s,{value:"7d",children:"weekly"}),(0,t.jsx)(s,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},213205,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let s={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var r=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(r.default,(0,t.default)({},e,{ref:a,icon:s}))});e.s(["UserAddOutlined",0,a],213205)},355619,e=>{"use strict";var t=e.i(602869);let l=async(e,l,s)=>{try{if(null===e||null===l)return;if(null!==s){let r=(await (0,t.modelAvailableCall)(s,e,l,!0,null,!0)).data.map(e=>e.id),a=[],n=[];return r.forEach(e=>{e.endsWith("/*")?a.push(e):n.push(e)}),[...a,...n]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["fetchAvailableModelsForTeamOrKey",0,l,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let t=e.replace("/*","");return`All ${t} models`}return e},"unfurlWildcardModelsInList",0,(e,t)=>{let l=[],s=[];return e.forEach(e=>{if(e.endsWith("/*")){let r=e.replace("/*",""),a=t.filter(e=>e.startsWith(r+"/"));s.push(...a),l.push(e)}else s.push(e)}),[...l,...s].filter((e,t,l)=>l.indexOf(e)===t)}])},350967,46757,e=>{"use strict";var t=e.i(290571),l=e.i(444755),s=e.i(673706),r=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},n={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},i={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},o={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,o,"gridColsMd",0,i,"gridColsSm",0,n],46757);let c=(0,s.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=r.default.forwardRef((e,s)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:p,children:h,className:x}=e,f=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),y=d(u,a),b=d(m,n),v=d(g,i),w=d(p,o),j=(0,l.tremorTwMerge)(y,b,v,w);return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(c("root"),"grid",j,x)},f),h)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),l=e.i(243652),s=e.i(602869),r=e.i(135214);let a=(0,l.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:l}=(0,r.default)();return(0,t.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,s.fetchMCPServers)(l,e),enabled:!!l})}])},699857,e=>{"use strict";var t=e.i(266027),l=e.i(243652),s=e.i(602869),r=e.i(135214);let a=(0,l.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,r.default)();return(0,t.useQuery)({queryKey:a.list(),queryFn:async()=>await (0,s.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),r=e.i(602869);e.s(["default",0,({onChange:e,value:a,className:n,accessToken:i,placeholder:o="Select vector stores",disabled:c=!1})=>{let[d,u]=(0,l.useState)([]),[m,g]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{(async()=>{if(i){g(!0);try{let e=await (0,r.vectorStoreListCall)(i);e.data&&u(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{g(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(s.Select,{mode:"multiple",placeholder:o,onChange:e,value:a,loading:m,className:n,allowClear:!0,options:d.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:c})})}])},75921,e=>{"use strict";var t=e.i(843476),l=e.i(266027),s=e.i(243652),r=e.i(602869),a=e.i(135214);let n=(0,s.createQueryKeys)("mcpAccessGroups");var i=e.i(500727),o=e.i(699857),c=e.i(199133),d=e.i(234713);let u="toolset:";e.s(["default",0,({onChange:e,value:s,className:m,accessToken:g,placeholder:p="Select MCP servers",disabled:h=!1,teamId:x,allowNoMcpServers:f=!1,allowAllProxyMcpServers:y=!1})=>{let{data:b=[],isLoading:v}=(0,i.useMCPServers)(x),{data:w=[],isLoading:j}=(()=>{let{accessToken:e}=(0,a.default)();return(0,l.useQuery)({queryKey:n.list({}),queryFn:async()=>await (0,r.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:N=[],isLoading:S}=(0,o.useMCPToolsets)(),k=new Set(w),C=[...w.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...b.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...N.map(e=>({label:e.toolset_name,value:`${u}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],_={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...s?.servers||[],...s?.accessGroups||[],...(s?.toolsets||[]).map(e=>`${u}${e}`)],L=f&&E.includes(d.NO_MCP_SERVERS_SENTINEL),R=E.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,t.jsx)("div",{children:(0,t.jsxs)(c.Select,{mode:"multiple",placeholder:p,onChange:t=>{if(y&&t.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[d.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(f&&t.includes(d.NO_MCP_SERVERS_SENTINEL))return void e({servers:[d.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let l=t.filter(e=>e.startsWith(u)).map(e=>e.slice(u.length)),s=t.filter(e=>!e.startsWith(u));e({servers:s.filter(e=>!k.has(e)),accessGroups:s.filter(e=>k.has(e)),toolsets:l})},value:E,loading:v||j||S,className:m,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:h,filterOption:(e,t)=>t?.value===d.NO_MCP_SERVERS_SENTINEL||t?.value===d.ALL_PROXY_MCP_SERVERS_SENTINEL||(C.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(y||R)&&(0,t.jsx)(c.Select.Option,{value:d.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,t.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},d.ALL_PROXY_MCP_SERVERS_SENTINEL),f&&(0,t.jsx)(c.Select.Option,{value:d.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,t.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},d.NO_MCP_SERVERS_SENTINEL),C.map(e=>(0,t.jsx)(c.Select.Option,{value:e.value,label:e.label,disabled:L||R,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:_[e.type],flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:_[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)},988297,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,l],988297)},246349,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,t])},797672,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,l],797672)},992619,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(779241),r=e.i(599724),a=e.i(199133),n=e.i(983561),i=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:g,showLabel:p=!0,labelText:h="Select Model"})=>{let[x,f]=(0,l.useState)(o),[y,b]=(0,l.useState)(!1),[v,w]=(0,l.useState)([]),j=(0,l.useRef)(null);return(0,l.useEffect)(()=>{f(o)},[o]),(0,l.useEffect)(()=>{e&&(async()=>{try{let t=await (0,i.fetchAvailableModels)(e);t.length>0&&w(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[p&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(n.RobotOutlined,{className:"mr-2"})," ",h]}),(0,t.jsx)(a.Select,{value:x,placeholder:c,onChange:e=>{"custom"===e?(b(!0),f(void 0)):(b(!1),f(e),d&&d(e))},options:[...Array.from(new Set(v.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${g||""}`,disabled:u}),y&&(0,t.jsx)(s.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{j.current&&clearTimeout(j.current),j.current=setTimeout(()=>{f(e),d&&d(e)},500)},disabled:u})]})}])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},531516,696609,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(536916),r=e.i(599724),a=e.i(409797),n=e.i(246349),n=n;let i=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let l=e.toLowerCase();if(d.test(l))return"read";if(i.test(l))return"delete";if(c.test(l))return"update";if(o.test(l))return"create";if(t){let e=t.toLowerCase();if(d.test(e))return"read";if(i.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let l of e)t[u(l.name,l.description)].push(l);return t}let g={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,g,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let p=["read","create","update","delete","unknown"],h={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},x={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},f={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:i,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[u,y]=(0,l.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),b=(0,l.useMemo)(()=>m(e),[e]),v=(0,l.useMemo)(()=>new Set(void 0===i?e.map(e=>e.name):i),[i,e]),w=e=>{if(c)return;let t=new Set(v);t.has(e)?t.delete(e):t.add(e),o(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:p.map(e=>{let l,i=b[e];if(0===i.length)return null;if(d){let e=d.toLowerCase();if(!i.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=g[e],p=(l=b[e]).length>0&&l.every(e=>v.has(e.name)),j=(e=>{let t=b[e];if(0===t.length)return!1;let l=t.filter(e=>v.has(e.name)).length;return l>0&&l{y(t=>({...t,[e]:!t[e]}))},children:[N?(0,t.jsx)(n.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(a.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${h[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[i.filter(e=>v.has(e.name)).length,"/",i.length," allowed"]})]}),!c&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:p?"All on":j?"Partial":"All off"}),(0,t.jsx)(s.Checkbox,{checked:p,indeterminate:j,onChange:t=>((e,t)=>{if(c)return;let l=new Set(v);for(let s of b[e])t?l.add(s.name):l.delete(s.name);o(Array.from(l))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!N&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!N&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:i.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let l,a=(l=e.name,v.has(l));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${a?"":"opacity-60"}`,onClick:()=>w(e.name),children:[(0,t.jsx)(s.Checkbox,{checked:a,onChange:()=>w(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(r.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${a?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:a?"on":"off"})]},e.name)})})]},e)})})}],531516)},695411,e=>{"use strict";var t=e.i(602869);let l=async e=>{try{let l=await (0,t.modelHubCall)(e);if(l?.data.length>0){let e=l.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,t)=>e.model_group.localeCompare(t.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,l])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},107233,e=>{"use strict";var t=e.i(603908);e.s(["Plus",()=>t.default])},37727,e=>{"use strict";var t=e.i(841947);e.s(["X",()=>t.default])},158392,63209,e=>{"use strict";var t=e.i(843476),l=e.i(311451);let s={ttl:3600,lowest_latency_buffer:0},r=({routingStrategyArgs:e})=>{let r={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||s).map(([e,s])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r[e]||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"})]})},a=({routerSettings:e,routerFieldsMetadata:s})=>(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,r])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:s[e]?.ui_field_name||e}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s[e]?.field_description||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:null==r||"null"===r?"":"object"==typeof r?JSON.stringify(r,null,2):r?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var n=e.i(199133);let i=({selectedStrategy:e,availableStrategies:l,routingStrategyDescriptions:s,routerFieldsMetadata:r,onStrategyChange:a})=>(0,t.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r.routing_strategy?.field_description||""})]}),(0,t.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,t.jsx)(n.Select,{value:e,onChange:a,style:{width:"100%"},size:"large",children:l.map(e=>(0,t.jsx)(n.Select.Option,{value:e,label:e,children:(0,t.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,t.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),s[e]&&(0,t.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:s[e]})]})},e))})})]});var o=e.i(790848);let c=({enabled:e,routerFieldsMetadata:l,onToggle:s})=>(0,t.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:l.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,t.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[l.enable_tag_filtering?.field_description||"",l.enable_tag_filtering?.link&&(0,t.jsxs)(t.Fragment,{children:[" ",(0,t.jsx)("a",{href:l.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,t.jsx)(o.Switch,{checked:e,onChange:s,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:l,routerFieldsMetadata:s,availableRoutingStrategies:n,routingStrategyDescriptions:o})=>(0,t.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),n.length>0&&(0,t.jsx)(i,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:n,routingStrategyDescriptions:o,routerFieldsMetadata:s,onStrategyChange:t=>{l({...e,selectedStrategy:t})}}),(0,t.jsx)(c,{enabled:e.enableTagFiltering,routerFieldsMetadata:s,onToggle:t=>{l({...e,enableTagFiltering:t})}})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,t.jsx)(r,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,t.jsx)(a,{routerSettings:e.routerSettings,routerFieldsMetadata:s})]})],158392);var d=e.i(361653);e.s(["AlertCircle",()=>d.default],63209)},425063,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,t],425063)},419470,e=>{"use strict";var t=e.i(843476),l=e.i(994388),s=e.i(653496),r=e.i(107233),a=e.i(271645),n=e.i(888259),i=e.i(199133),o=e.i(592968),c=e.i(63209),d=e.i(425063),u=e.i(37727);function m({group:e,onChange:l,availableModels:s,maxFallbacks:r}){let a=s.filter(t=>t!==e.primaryModel),n=e.fallbackModels.length{let s=[...e.fallbackModels];s.includes(t)&&(s=s.filter(e=>e!==t)),l({...e,primaryModel:t,fallbackModels:s})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:s.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,t.jsx)(c.AlertCircle,{className:"w-4 h-4"}),(0,t.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,t.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,t.jsx)(d.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,t.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,t.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,t.jsx)("span",{className:"text-red-500",children:"*"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",r," fallbacks at a time)"]})]}),(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(i.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:n?"Select fallback models to add...":`Maximum ${r} fallbacks reached`,value:e.fallbackModels,onChange:t=>{let s=t.slice(0,r);l({...e,fallbackModels:s})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:a.map(e=>({label:e,value:e})),optionRender:(l,s)=>{let r=e.fallbackModels.includes(l.value),a=r?e.fallbackModels.indexOf(l.value)+1:null;return(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[r&&null!==a&&(0,t.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:a}),(0,t.jsx)("span",{children:l.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:n?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${r} used)`:`Maximum ${r} fallbacks reached. Remove some to add more.`})]}),(0,t.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,t.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,t.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,t.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((s,r)=>(0,t.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,t.jsx)("span",{className:"text-xs font-bold",children:r+1})}),(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"font-medium text-gray-800",children:s})})]}),(0,t.jsx)("button",{type:"button",onClick:()=>{let t;return t=e.fallbackModels.filter((e,t)=>t!==r),void l({...e,fallbackModels:t})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,t.jsx)(u.X,{className:"w-4 h-4"})})]},`${s}-${r}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:i,availableModels:o,maxFallbacks:c=10,maxGroups:d=5}){let[u,g]=(0,a.useState)(e.length>0?e[0].id:"1");(0,a.useEffect)(()=>{e.length>0?e.some(e=>e.id===u)||g(e[0].id):g("1")},[e]);let p=()=>{if(e.length>=d)return;let t=Date.now().toString();i([...e,{id:t,primaryModel:null,fallbackModels:[]}]),g(t)},h=t=>{i(e.map(e=>e.id===t.id?t:e))},x=e.map((l,s)=>{let r=l.primaryModel?l.primaryModel:`Group ${s+1}`;return{key:l.id,label:r,closable:e.length>1,children:(0,t.jsx)(m,{group:l,onChange:h,availableModels:o,maxFallbacks:c})}});return 0===e.length?(0,t.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,t.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,t.jsx)(l.Button,{variant:"primary",onClick:p,icon:()=>(0,t.jsx)(r.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,t.jsx)(s.Tabs,{type:"editable-card",activeKey:u,onChange:g,onEdit:(t,l)=>{"add"===l?p():"remove"===l&&e.length>1&&(t=>{if(1===e.length)return n.default.warning("At least one group is required");let l=e.filter(e=>e.id!==t);i(l),u===t&&l.length>0&&g(l[l.length-1].id)})(t)},items:x,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=d})}],419470)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-~nw1zmks9_4.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-~nw1zmks9_4.js new file mode 100644 index 00000000000..6504ddd6e5e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0-~nw1zmks9_4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,737434,e=>{"use strict";var t=e.i(184163);e.s(["DownloadOutlined",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),i=e.i(201072),n=e.i(121229),s=e.i(726289),o=e.i(864517),a=e.i(343794),l=e.i(529681),c=e.i(242064),u=e.i(931067),d=e.i(209428),h=e.i(703923),f={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},p=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),i=!1;e.current.forEach(function(e){if(e){i=!0;var n=e.style;n.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(n.transitionDuration="0s, 0s")}}),i&&(r.current=Date.now())}),e.current},g=e.i(410160),m=e.i(392221),y=e.i(654310),_=0,b=(0,y.default)();let k=function(e){var r=t.useState(),i=(0,m.default)(r,2),n=i[0],s=i[1];return t.useEffect(function(){var e;s("rc_progress_".concat((b?(e=_,_+=1):e="TEST_OR_SSR",e)))},[]),e||n};var v=function(e){var r=e.bg,i=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},i)};function C(e,t){return Object.keys(e).map(function(r){var i=parseFloat(r),n="".concat(Math.floor(i*t),"%");return"".concat(e[r]," ").concat(n)})}var x=t.forwardRef(function(e,r){var i=e.prefixCls,n=e.color,s=e.gradientId,o=e.radius,a=e.style,l=e.ptg,c=e.strokeLinecap,u=e.strokeWidth,d=e.size,h=e.gapDegree,f=n&&"object"===(0,g.default)(n),p=d/2,m=t.createElement("circle",{className:"".concat(i,"-circle-path"),r:o,cx:p,cy:p,stroke:f?"#FFF":void 0,strokeLinecap:c,strokeWidth:u,opacity:+(0!==l),style:a,ref:r});if(!f)return m;var y="".concat(s,"-conic"),_=C(n,(360-h)/360),b=C(n,1),k="conic-gradient(from ".concat(h?"".concat(180+h/2,"deg"):"0deg",", ").concat(_.join(", "),")"),x="linear-gradient(to ".concat(h?"bottom":"top",", ").concat(b.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:y},m),t.createElement("foreignObject",{x:0,y:0,width:d,height:d,mask:"url(#".concat(y,")")},t.createElement(v,{bg:x},t.createElement(v,{bg:k}))))}),E=function(e,t,r,i,n,s,o,a,l,c){var u=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,d=(100-i)/100*t;return"round"===l&&100!==i&&(d+=c/2)>=t&&(d=t-.01),{stroke:"string"==typeof a?a:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:d+u,transform:"rotate(".concat(n+r/100*360*((360-s)/360)+(0===s?0:({bottom:0,top:180,left:90,right:-90})[o]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},w=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function S(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let $=function(e){var r,i,n,s,o=(0,d.default)((0,d.default)({},f),e),l=o.id,c=o.prefixCls,m=o.steps,y=o.strokeWidth,_=o.trailWidth,b=o.gapDegree,v=void 0===b?0:b,C=o.gapPosition,$=o.trailColor,O=o.strokeLinecap,R=o.style,I=o.className,A=o.strokeColor,j=o.percent,D=(0,h.default)(o,w),T=k(l),L="".concat(T,"-gradient"),F=50-y/2,z=2*Math.PI*F,M=v>0?90+v/2:-90,P=(360-v)/360*z,N="object"===(0,g.default)(m)?m:{count:m,gap:2},W=N.count,B=N.gap,U=S(j),H=S(A),q=H.find(function(e){return e&&"object"===(0,g.default)(e)}),K=q&&"object"===(0,g.default)(q)?"butt":O,X=E(z,P,0,100,M,v,C,$,K,y),Q=p();return t.createElement("svg",(0,u.default)({className:(0,a.default)("".concat(c,"-circle"),I),viewBox:"0 0 ".concat(100," ").concat(100),style:R,id:l,role:"presentation"},D),!W&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:F,cx:50,cy:50,stroke:$,strokeLinecap:K,strokeWidth:_||y,style:X}),W?(r=Math.round(W*(U[0]/100)),i=100/W,n=0,Array(W).fill(null).map(function(e,s){var o=s<=r-1?H[0]:$,a=o&&"object"===(0,g.default)(o)?"url(#".concat(L,")"):void 0,l=E(z,P,n,i,M,v,C,o,"butt",y,B);return n+=(P-l.strokeDashoffset+B)*100/P,t.createElement("circle",{key:s,className:"".concat(c,"-circle-path"),r:F,cx:50,cy:50,stroke:a,strokeWidth:y,opacity:1,style:l,ref:function(e){Q[s]=e}})})):(s=0,U.map(function(e,r){var i=H[r]||H[H.length-1],n=E(z,P,s,e,M,v,C,i,K,y);return s+=e,t.createElement(x,{key:r,color:i,ptg:e,radius:F,prefixCls:c,gradientId:L,style:n,strokeLinecap:K,strokeWidth:y,gapDegree:v,ref:function(e){Q[r]=e},size:100})}).reverse()))};var O=e.i(491816);e.i(765846);var R=e.i(896091);function I(e){return!e||e<0?0:e>100?100:e}function A({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let j=(e,t,r)=>{var i,n,s,o;let a=-1,l=-1;if("step"===t){let t=r.steps,i=r.strokeWidth;"string"==typeof e||void 0===e?(a="small"===e?2:14,l=null!=i?i:8):"number"==typeof e?[a,l]=[e,e]:[a=14,l=8]=Array.isArray(e)?e:[e.width,e.height],a*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?l=t||("small"===e?6:8):"number"==typeof e?[a,l]=[e,e]:[a=-1,l=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[a,l]="small"===e?[60,60]:[120,120]:"number"==typeof e?[a,l]=[e,e]:Array.isArray(e)&&(a=null!=(n=null!=(i=e[0])?i:e[1])?n:120,l=null!=(o=null!=(s=e[0])?s:e[1])?o:120));return[a,l]},D=e=>{let{prefixCls:r,trailColor:i=null,strokeLinecap:n="round",gapPosition:s,gapDegree:o,width:l=120,type:c,children:u,success:d,size:h=l,steps:f}=e,[p,g]=j(h,"circle"),{strokeWidth:m}=e;void 0===m&&(m=Math.max(3/p*100,6));let y=t.useMemo(()=>o||0===o?o:"dashboard"===c?75:void 0,[o,c]),_=(({percent:e,success:t,successPercent:r})=>{let i=I(A({success:t,successPercent:r}));return[i,I(I(e)-i)]})(e),b="[object Object]"===Object.prototype.toString.call(e.strokeColor),k=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||R.presetPrimaryColors.green,t||null]})({success:d,strokeColor:e.strokeColor}),v=(0,a.default)(`${r}-inner`,{[`${r}-circle-gradient`]:b}),C=t.createElement($,{steps:f,percent:f?_[1]:_,strokeWidth:m,trailWidth:m,strokeColor:f?k[1]:k,strokeLinecap:n,trailColor:i,prefixCls:r,gapDegree:y,gapPosition:s||"dashboard"===c&&"bottom"||void 0}),x=p<=20,E=t.createElement("div",{className:v,style:{width:p,height:g,fontSize:.15*p+6}},C,!x&&u);return x?t.createElement(O.default,{title:u},E):E};e.i(296059);var T=e.i(694758),L=e.i(915654),F=e.i(183293),z=e.i(246422),M=e.i(838378);let P="--progress-line-stroke-color",N="--progress-percent",W=e=>{let t=e?"100%":"-100%";return new T.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},B=(0,z.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,M.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,F.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${P})`]},height:"100%",width:`calc(1 / var(${N}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,L.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:W(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:W(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var U=function(e,t){var r={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(r[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,i=Object.getOwnPropertySymbols(e);nt.indexOf(i[n])&&Object.prototype.propertyIsEnumerable.call(e,i[n])&&(r[i[n]]=e[i[n]]);return r};let H=e=>{let{prefixCls:r,direction:i,percent:n,size:s,strokeWidth:o,strokeColor:l,strokeLinecap:c="round",children:u,trailColor:d=null,percentPosition:h,success:f}=e,{align:p,type:g}=h,m=l&&"string"!=typeof l?((e,t)=>{let{from:r=R.presetPrimaryColors.blue,to:i=R.presetPrimaryColors.blue,direction:n="rtl"===t?"to left":"to right"}=e,s=U(e,["from","to","direction"]);if(0!==Object.keys(s).length){let e,t=(e=[],Object.keys(s).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:s[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${n}, ${t})`;return{background:r,[P]:r}}let o=`linear-gradient(${n}, ${r}, ${i})`;return{background:o,[P]:o}})(l,i):{[P]:l,background:l},y="square"===c||"butt"===c?0:void 0,[_,b]=j(null!=s?s:[-1,o||("small"===s?6:8)],"line",{strokeWidth:o}),k=Object.assign(Object.assign({width:`${I(n)}%`,height:b,borderRadius:y},m),{[N]:I(n)/100}),v=A(e),C={width:`${I(v)}%`,height:b,borderRadius:y,backgroundColor:null==f?void 0:f.strokeColor},x=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:d||void 0,borderRadius:y}},t.createElement("div",{className:(0,a.default)(`${r}-bg`,`${r}-bg-${g}`),style:k},"inner"===g&&u),void 0!==v&&t.createElement("div",{className:`${r}-success-bg`,style:C})),E="outer"===g&&"start"===p,w="outer"===g&&"end"===p;return"outer"===g&&"center"===p?t.createElement("div",{className:`${r}-layout-bottom`},x,u):t.createElement("div",{className:`${r}-outer`,style:{width:_<0?"100%":_}},E&&u,x,w&&u)},q=e=>{let{size:r,steps:i,rounding:n=Math.round,percent:s=0,strokeWidth:o=8,strokeColor:l,trailColor:c=null,prefixCls:u,children:d}=e,h=n(s/100*i),[f,p]=j(null!=r?r:["small"===r?2:14,o],"step",{steps:i,strokeWidth:o}),g=f/i,m=Array.from({length:i});for(let e=0;et.indexOf(i)&&(r[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,i=Object.getOwnPropertySymbols(e);nt.indexOf(i[n])&&Object.prototype.propertyIsEnumerable.call(e,i[n])&&(r[i[n]]=e[i[n]]);return r};let X=["normal","exception","active","success"],Q=t.forwardRef((e,u)=>{let d,{prefixCls:h,className:f,rootClassName:p,steps:g,strokeColor:m,percent:y=0,size:_="default",showInfo:b=!0,type:k="line",status:v,format:C,style:x,percentPosition:E={}}=e,w=K(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:S="end",type:$="outer"}=E,O=Array.isArray(m)?m[0]:m,R="string"==typeof m||Array.isArray(m)?m:void 0,T=t.useMemo(()=>{if(O){let e="string"==typeof O?O:Object.values(O)[0];return new r.FastColor(e).isLight()}return!1},[m]),L=t.useMemo(()=>{var t,r;let i=A(e);return Number.parseInt(void 0!==i?null==(t=null!=i?i:0)?void 0:t.toString():null==(r=null!=y?y:0)?void 0:r.toString(),10)},[y,e.success,e.successPercent]),F=t.useMemo(()=>!X.includes(v)&&L>=100?"success":v||"normal",[v,L]),{getPrefixCls:z,direction:M,progress:P}=t.useContext(c.ConfigContext),N=z("progress",h),[W,U,Q]=B(N),J="line"===k,V=J&&!g,Y=t.useMemo(()=>{let r;if(!b)return null;let l=A(e),c=C||(e=>`${e}%`),u=J&&T&&"inner"===$;return"inner"===$||C||"exception"!==F&&"success"!==F?r=c(I(y),I(l)):"exception"===F?r=J?t.createElement(s.default,null):t.createElement(o.default,null):"success"===F&&(r=J?t.createElement(i.default,null):t.createElement(n.default,null)),t.createElement("span",{className:(0,a.default)(`${N}-text`,{[`${N}-text-bright`]:u,[`${N}-text-${S}`]:V,[`${N}-text-${$}`]:V}),title:"string"==typeof r?r:void 0},r)},[b,y,L,F,k,N,C]);"line"===k?d=g?t.createElement(q,Object.assign({},e,{strokeColor:R,prefixCls:N,steps:"object"==typeof g?g.count:g}),Y):t.createElement(H,Object.assign({},e,{strokeColor:O,prefixCls:N,direction:M,percentPosition:{align:S,type:$}}),Y):("circle"===k||"dashboard"===k)&&(d=t.createElement(D,Object.assign({},e,{strokeColor:O,prefixCls:N,progressStatus:F}),Y));let Z=(0,a.default)(N,`${N}-status-${F}`,{[`${N}-${"dashboard"===k&&"circle"||k}`]:"line"!==k,[`${N}-inline-circle`]:"circle"===k&&j(_,"circle")[0]<=20,[`${N}-line`]:V,[`${N}-line-align-${S}`]:V,[`${N}-line-position-${$}`]:V,[`${N}-steps`]:g,[`${N}-show-info`]:b,[`${N}-${_}`]:"string"==typeof _,[`${N}-rtl`]:"rtl"===M},null==P?void 0:P.className,f,p,U,Q);return W(t.createElement("div",Object.assign({ref:u,style:Object.assign(Object.assign({},null==P?void 0:P.style),x),className:Z,role:"progressbar","aria-valuenow":L,"aria-valuemin":0,"aria-valuemax":100},(0,l.default)(w,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),d))});e.s(["default",0,Q],309821)},993914,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"}}]},name:"file-text",theme:"outlined"};var n=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(n.default,(0,t.default)({},e,{ref:s,icon:i}))});e.s(["FileTextOutlined",0,s],993914)},59935,(e,t,r)=>{var i;let n;e.e,i=function e(){var t,r="u">typeof self?self:"u">typeof window?window:void 0!==r?r:{},i=!r.document&&!!r.postMessage,n=r.IS_PAPA_WORKER||!1,s={},o=0,a={};function l(e){this._handle=null,this._finished=!1,this._completed=!1,this._halted=!1,this._input=null,this._baseIndex=0,this._partialLine="",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},(function(e){var t=b(e);t.chunkSize=parseInt(t.chunkSize),e.step||e.chunk||(t.chunkSize=null),this._handle=new f(t),(this._handle.streamer=this)._config=t}).call(this,e),this.parseChunk=function(e,t){var i=parseInt(this._config.skipFirstNLines)||0;if(this.isFirstChunk&&0=this._config.preview,n)r.postMessage({results:s,workerId:a.WORKER_ID,finished:i});else if(v(this._config.chunk)&&!t){if(this._config.chunk(s,this._handle),this._handle.paused()||this._handle.aborted())return void(this._halted=!0);this._completeResults=s=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(s.data),this._completeResults.errors=this._completeResults.errors.concat(s.errors),this._completeResults.meta=s.meta),this._completed||!i||!v(this._config.complete)||s&&s.meta.aborted||(this._config.complete(this._completeResults,this._input),this._completed=!0),i||s&&s.meta.paused||this._nextChunk(),s}this._halted=!0},this._sendError=function(e){v(this._config.error)?this._config.error(e):n&&this._config.error&&r.postMessage({workerId:a.WORKER_ID,error:e,finished:!1})}}function c(e){var t;(e=e||{}).chunkSize||(e.chunkSize=a.RemoteChunkSize),l.call(this,e),this._nextChunk=i?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(e){this._input=e,this._nextChunk()},this._readChunk=function(){if(this._finished)this._chunkLoaded();else{if(t=new XMLHttpRequest,this._config.withCredentials&&(t.withCredentials=this._config.withCredentials),i||(t.onload=k(this._chunkLoaded,this),t.onerror=k(this._chunkError,this)),t.open(this._config.downloadRequestBody?"POST":"GET",this._input,!i),this._config.downloadRequestHeaders){var e,r,n=this._config.downloadRequestHeaders;for(r in n)t.setRequestHeader(r,n[r])}this._config.chunkSize&&(e=this._start+this._config.chunkSize-1,t.setRequestHeader("Range","bytes="+this._start+"-"+e));try{t.send(this._config.downloadRequestBody)}catch(e){this._chunkError(e.message)}i&&0===t.status&&this._chunkError()}},this._chunkLoaded=function(){let e;4===t.readyState&&(t.status<200||400<=t.status?this._chunkError():(this._start+=this._config.chunkSize||t.responseText.length,this._finished=!this._config.chunkSize||this._start>=(null!==(e=(e=t).getResponseHeader("Content-Range"))?parseInt(e.substring(e.lastIndexOf("/")+1)):-1),this.parseChunk(t.responseText)))},this._chunkError=function(e){e=t.statusText||e,this._sendError(Error(e))}}function u(e){(e=e||{}).chunkSize||(e.chunkSize=a.LocalChunkSize),l.call(this,e);var t,r,i="u">typeof FileReader;this.stream=function(e){this._input=e,r=e.slice||e.webkitSlice||e.mozSlice,i?((t=new FileReader).onload=k(this._chunkLoaded,this),t.onerror=k(this._chunkError,this)):t=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(e.target.result)},this._chunkError=function(){this._sendError(t.error)}}function d(e){var t;l.call(this,e=e||{}),this.stream=function(e){return t=e,this._nextChunk()},this._nextChunk=function(){var e,r;if(!this._finished)return t=(e=this._config.chunkSize)?(r=t.substring(0,e),t.substring(e)):(r=t,""),this._finished=!t,this.parseChunk(r)}}function h(e){l.call(this,e=e||{});var t=[],r=!0,i=!1;this.pause=function(){l.prototype.pause.apply(this,arguments),this._input.pause()},this.resume=function(){l.prototype.resume.apply(this,arguments),this._input.resume()},this.stream=function(e){this._input=e,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError)},this._checkIsFinished=function(){i&&1===t.length&&(this._finished=!0)},this._nextChunk=function(){this._checkIsFinished(),t.length?this.parseChunk(t.shift()):r=!0},this._streamData=k(function(e){try{t.push("string"==typeof e?e:e.toString(this._config.encoding)),r&&(r=!1,this._checkIsFinished(),this.parseChunk(t.shift()))}catch(e){this._streamError(e)}},this),this._streamError=k(function(e){this._streamCleanUp(),this._sendError(e)},this),this._streamEnd=k(function(){this._streamCleanUp(),i=!0,this._streamData("")},this),this._streamCleanUp=k(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError)},this)}function f(e){var t,r,i,n,s=/^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/,o=/^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/,l=this,c=0,u=0,d=!1,h=!1,f=[],m={data:[],errors:[],meta:{}};function y(t){return"greedy"===e.skipEmptyLines?""===t.join("").trim():1===t.length&&0===t[0].length}function _(){if(m&&i&&(C("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+a.DefaultDelimiter+"'"),i=!1),e.skipEmptyLines&&(m.data=m.data.filter(function(e){return!y(e)})),k()){if(m)if(Array.isArray(m.data[0])){for(var t,r=0;k()&&r(e.dynamicTypingFunction&&void 0===e.dynamicTyping[t]&&(e.dynamicTyping[t]=e.dynamicTypingFunction(t)),!0===(e.dynamicTyping[t]||e.dynamicTyping))?"true"===r||"TRUE"===r||"false"!==r&&"FALSE"!==r&&((e=>{if(s.test(e)&&-0x20000000000000<(e=parseFloat(e))&&e<0x20000000000000)return 1})(r)?parseFloat(r):o.test(r)?new Date(r):""===r?null:r):r)(a=e.header?n>=f.length?"__parsed_extra":f[n]:a,l=e.transform?e.transform(l,a):l);"__parsed_extra"===a?(i[a]=i[a]||[],i[a].push(l)):i[a]=l}return e.header&&(n>f.length?C("FieldMismatch","TooManyFields","Too many fields: expected "+f.length+" fields but parsed "+n,u+r):ne.preview?r.abort():(m.data=m.data[0],n(m,l))))}),this.parse=function(n,s,o){var l=e.quoteChar||'"',l=(e.newline||(e.newline=this.guessLineEndings(n,l)),i=!1,e.delimiter?v(e.delimiter)&&(e.delimiter=e.delimiter(n),m.meta.delimiter=e.delimiter):((l=((t,r,i,n,s)=>{var o,l,c,u;s=s||[","," ","|",";",a.RECORD_SEP,a.UNIT_SEP];for(var d=0;d=r.length/2?"\r\n":"\r"}}function p(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function g(e){var t=(e=e||{}).delimiter,r=e.newline,i=e.comments,n=e.step,s=e.preview,o=e.fastMode,l=null,c=!1,u=null==e.quoteChar?'"':e.quoteChar,d=u;if(void 0!==e.escapeChar&&(d=e.escapeChar),("string"!=typeof t||-1=s)return M(!0);break}E.push({type:"Quotes",code:"InvalidQuotes",message:"Trailing quote on quoted field is malformed",row:x.length,index:h}),j++}}else if(i&&0===w.length&&a.substring(h,h+k)===i){if(-1===I)return M();h=I+b,I=a.indexOf(r,h),R=a.indexOf(t,h)}else if(-1!==R&&(R=s)return M(!0)}return F();function T(e){x.push(e),S=h}function L(e){return -1!==e&&(e=a.substring(j+1,e))&&""===e.trim()?e.length:0}function F(e){return m||(void 0===e&&(e=a.substring(h)),w.push(e),h=y,T(w),C&&P()),M()}function z(e){h=e,T(w),w=[],I=a.indexOf(r,h)}function M(i){if(e.header&&!g&&x.length&&!c){var n=x[0],s=Object.create(null),o=new Set(n);let t=!1;for(let r=0;r{if("object"==typeof t){if("string"!=typeof t.delimiter||a.BAD_DELIMITERS.filter(function(e){return -1!==t.delimiter.indexOf(e)}).length||(n=t.delimiter),("boolean"==typeof t.quotes||"function"==typeof t.quotes||Array.isArray(t.quotes))&&(r=t.quotes),"boolean"!=typeof t.skipEmptyLines&&"string"!=typeof t.skipEmptyLines||(c=t.skipEmptyLines),"string"==typeof t.newline&&(s=t.newline),"string"==typeof t.quoteChar&&(o=t.quoteChar),"boolean"==typeof t.header&&(i=t.header),Array.isArray(t.columns)){if(0===t.columns.length)throw Error("Option columns is empty");u=t.columns}void 0!==t.escapeChar&&(l=t.escapeChar+o),t.escapeFormulae instanceof RegExp?d=t.escapeFormulae:"boolean"==typeof t.escapeFormulae&&t.escapeFormulae&&(d=/^[=+\-@\t\r].*$/)}})(),RegExp(p(o),"g"));if("string"==typeof e&&(e=JSON.parse(e)),Array.isArray(e)){if(!e.length||Array.isArray(e[0]))return f(null,e,c);if("object"==typeof e[0])return f(u||Object.keys(e[0]),e,c)}else if("object"==typeof e)return"string"==typeof e.data&&(e.data=JSON.parse(e.data)),Array.isArray(e.data)&&(e.fields||(e.fields=e.meta&&e.meta.fields||u),e.fields||(e.fields=Array.isArray(e.data[0])?e.fields:"object"==typeof e.data[0]?Object.keys(e.data[0]):[]),Array.isArray(e.data[0])||"object"==typeof e.data[0]||(e.data=[e.data])),f(e.fields||[],e.data||[],c);throw Error("Unable to serialize unrecognized input");function f(e,t,r){var o="",a=("string"==typeof e&&(e=JSON.parse(e)),"string"==typeof t&&(t=JSON.parse(t)),Array.isArray(e)&&0{for(var r=0;r{"use strict";let t=(0,e.i(475254).default)("chevron-down",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);e.s(["default",0,t])},246349,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,t])},174886,991124,e=>{"use strict";let t=(0,e.i(475254).default)("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);e.s(["default",0,t],991124),e.s(["Copy",0,t],174886)},560445,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(201072),r=e.i(726289),i=e.i(864517),s=e.i(562901),l=e.i(779573),n=e.i(343794),o=e.i(361275),c=e.i(244009),u=e.i(611935),d=e.i(763731),f=e.i(242064);e.i(296059);var m=e.i(915654),h=e.i(183293),g=e.i(246422);let p=(e,t,a,r,i)=>({background:e,border:`${(0,m.unit)(r.lineWidth)} ${r.lineType} ${t}`,[`${i}-icon`]:{color:a}}),v=(0,g.genStyleHooks)("Alert",e=>[(e=>{let{componentCls:t,motionDurationSlow:a,marginXS:r,marginSM:i,fontSize:s,fontSizeLG:l,lineHeight:n,borderRadiusLG:o,motionEaseInOutCirc:c,withDescriptionIconSize:u,colorText:d,colorTextHeading:f,withDescriptionPadding:m,defaultPadding:g}=e;return{[t]:Object.assign(Object.assign({},(0,h.resetComponent)(e)),{position:"relative",display:"flex",alignItems:"center",padding:g,wordWrap:"break-word",borderRadius:o,[`&${t}-rtl`]:{direction:"rtl"},[`${t}-content`]:{flex:1,minWidth:0},[`${t}-icon`]:{marginInlineEnd:r,lineHeight:0},"&-description":{display:"none",fontSize:s,lineHeight:n},"&-message":{color:f},[`&${t}-motion-leave`]:{overflow:"hidden",opacity:1,transition:`max-height ${a} ${c}, opacity ${a} ${c}, + padding-top ${a} ${c}, padding-bottom ${a} ${c}, + margin-bottom ${a} ${c}`},[`&${t}-motion-leave-active`]:{maxHeight:0,marginBottom:"0 !important",paddingTop:0,paddingBottom:0,opacity:0}}),[`${t}-with-description`]:{alignItems:"flex-start",padding:m,[`${t}-icon`]:{marginInlineEnd:i,fontSize:u,lineHeight:0},[`${t}-message`]:{display:"block",marginBottom:r,color:f,fontSize:l},[`${t}-description`]:{display:"block",color:d}},[`${t}-banner`]:{marginBottom:0,border:"0 !important",borderRadius:0}}})(e),(e=>{let{componentCls:t,colorSuccess:a,colorSuccessBorder:r,colorSuccessBg:i,colorWarning:s,colorWarningBorder:l,colorWarningBg:n,colorError:o,colorErrorBorder:c,colorErrorBg:u,colorInfo:d,colorInfoBorder:f,colorInfoBg:m}=e;return{[t]:{"&-success":p(i,r,a,e,t),"&-info":p(m,f,d,e,t),"&-warning":p(n,l,s,e,t),"&-error":Object.assign(Object.assign({},p(u,c,o,e,t)),{[`${t}-description > pre`]:{margin:0,padding:0}})}}})(e),(e=>{let{componentCls:t,iconCls:a,motionDurationMid:r,marginXS:i,fontSizeIcon:s,colorIcon:l,colorIconHover:n}=e;return{[t]:{"&-action":{marginInlineStart:i},[`${t}-close-icon`]:{marginInlineStart:i,padding:0,overflow:"hidden",fontSize:s,lineHeight:(0,m.unit)(s),backgroundColor:"transparent",border:"none",outline:"none",cursor:"pointer",[`${a}-close`]:{color:l,transition:`color ${r}`,"&:hover":{color:n}}},"&-close-text":{color:l,transition:`color ${r}`,"&:hover":{color:n}}}}})(e)],e=>({withDescriptionIconSize:e.fontSizeHeading3,defaultPadding:`${e.paddingContentVerticalSM}px 12px`,withDescriptionPadding:`${e.paddingMD}px ${e.paddingContentHorizontalLG}px`}));var y=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(a[r[i]]=e[r[i]]);return a};let x={success:a.default,info:l.default,error:r.default,warning:s.default},b=e=>{let{icon:a,prefixCls:r,type:i}=e,s=x[i]||null;return a?(0,d.replaceElement)(a,t.createElement("span",{className:`${r}-icon`},a),()=>({className:(0,n.default)(`${r}-icon`,a.props.className)})):t.createElement(s,{className:`${r}-icon`})},w=e=>{let{isClosable:a,prefixCls:r,closeIcon:s,handleClose:l,ariaProps:n}=e,o=!0===s||void 0===s?t.createElement(i.default,null):s;return a?t.createElement("button",Object.assign({type:"button",onClick:l,className:`${r}-close-icon`,tabIndex:0},n),o):null},k=t.forwardRef((e,a)=>{let{description:r,prefixCls:i,message:s,banner:l,className:d,rootClassName:m,style:h,onMouseEnter:g,onMouseLeave:p,onClick:x,afterClose:k,showIcon:_,closable:j,closeText:E,closeIcon:S,action:N,id:C}=e,M=y(e,["description","prefixCls","message","banner","className","rootClassName","style","onMouseEnter","onMouseLeave","onClick","afterClose","showIcon","closable","closeText","closeIcon","action","id"]),[I,P]=t.useState(!1),O=t.useRef(null);t.useImperativeHandle(a,()=>({nativeElement:O.current}));let{getPrefixCls:T,direction:L,closable:R,closeIcon:z,className:$,style:A}=(0,f.useComponentConfig)("alert"),D=T("alert",i),[B,F,V]=v(D),H=t=>{var a;P(!0),null==(a=e.onClose)||a.call(e,t)},U=t.useMemo(()=>void 0!==e.type?e.type:l?"warning":"info",[e.type,l]),q=t.useMemo(()=>"object"==typeof j&&!!j.closeIcon||!!E||("boolean"==typeof j?j:!1!==S&&null!=S||!!R),[E,S,j,R]),K=!!l&&void 0===_||_,G=(0,n.default)(D,`${D}-${U}`,{[`${D}-with-description`]:!!r,[`${D}-no-icon`]:!K,[`${D}-banner`]:!!l,[`${D}-rtl`]:"rtl"===L},$,d,m,V,F),Q=(0,c.default)(M,{aria:!0,data:!0}),W=t.useMemo(()=>"object"==typeof j&&j.closeIcon?j.closeIcon:E||(void 0!==S?S:"object"==typeof R&&R.closeIcon?R.closeIcon:z),[S,j,R,E,z]),Y=t.useMemo(()=>{let e=null!=j?j:R;if("object"==typeof e){let{closeIcon:t}=e;return y(e,["closeIcon"])}return{}},[j,R]);return B(t.createElement(o.default,{visible:!I,motionName:`${D}-motion`,motionAppear:!1,motionEnter:!1,onLeaveStart:e=>({maxHeight:e.offsetHeight}),onLeaveEnd:k},({className:a,style:i},l)=>t.createElement("div",Object.assign({id:C,ref:(0,u.composeRef)(O,l),"data-show":!I,className:(0,n.default)(G,a),style:Object.assign(Object.assign(Object.assign({},A),h),i),onMouseEnter:g,onMouseLeave:p,onClick:x,role:"alert"},Q),K?t.createElement(b,{description:r,icon:e.icon,prefixCls:D,type:U}):null,t.createElement("div",{className:`${D}-content`},s?t.createElement("div",{className:`${D}-message`},s):null,r?t.createElement("div",{className:`${D}-description`},r):null),N?t.createElement("div",{className:`${D}-action`},N):null,t.createElement(w,{isClosable:q,prefixCls:D,closeIcon:W,handleClose:H,ariaProps:Y}))))});var _=e.i(278409),j=e.i(233848),E=e.i(487806),S=e.i(479671),N=e.i(480002),C=e.i(868917);let M=function(e){function a(){var e,t,r;return(0,_.default)(this,a),t=a,r=arguments,t=(0,E.default)(t),(e=(0,N.default)(this,(0,S.default)()?Reflect.construct(t,r||[],(0,E.default)(this).constructor):t.apply(this,r))).state={error:void 0,info:{componentStack:""}},e}return(0,C.default)(a,e),(0,j.default)(a,[{key:"componentDidCatch",value:function(e,t){this.setState({error:e,info:t})}},{key:"render",value:function(){let{message:e,description:a,id:r,children:i}=this.props,{error:s,info:l}=this.state,n=(null==l?void 0:l.componentStack)||null,o=void 0===e?(s||"").toString():e;return s?t.createElement(k,{id:r,type:"error",message:o,description:t.createElement("pre",{style:{fontSize:"0.9em",overflowX:"auto"}},void 0===a?n:a)}):i}}])}(t.Component);k.ErrorBoundary=M,e.s(["Alert",0,k],560445)},621482,e=>{"use strict";var t=e.i(869230),a=e.i(992571),r=class extends t.QueryObserver{constructor(e,t){super(e,t)}bindMethods(){super.bindMethods(),this.fetchNextPage=this.fetchNextPage.bind(this),this.fetchPreviousPage=this.fetchPreviousPage.bind(this)}setOptions(e){e._type="infinite",super.setOptions(e)}getOptimisticResult(e){return e._type="infinite",super.getOptimisticResult(e)}fetchNextPage(e){return this.fetch({...e,meta:{fetchMore:{direction:"forward"}}})}fetchPreviousPage(e){return this.fetch({...e,meta:{fetchMore:{direction:"backward"}}})}createResult(e,t){let{state:r}=e,i=super.createResult(e,t),{isFetching:s,isRefetching:l,isError:n,isRefetchError:o}=i,c=r.fetchMeta?.fetchMore?.direction,u=n&&"forward"===c,d=s&&"forward"===c,f=n&&"backward"===c,m=s&&"backward"===c;return{...i,fetchNextPage:this.fetchNextPage,fetchPreviousPage:this.fetchPreviousPage,hasNextPage:(0,a.hasNextPage)(t,r.data),hasPreviousPage:(0,a.hasPreviousPage)(t,r.data),isFetchNextPageError:u,isFetchingNextPage:d,isFetchPreviousPageError:f,isFetchingPreviousPage:m,isRefetchError:o&&!u&&!f,isRefetching:l&&!d&&!m}}},i=e.i(469637);e.s(["useInfiniteQuery",0,function(e,t){return(0,i.useBaseQuery)(e,r,t)}],621482)},785242,270345,e=>{"use strict";var t=e.i(619273),a=e.i(621482),r=e.i(266027),i=e.i(912598),s=e.i(135214),l=e.i(602869);let n=async(e,t,a,r)=>"Admin"!=a&&"Admin Viewer"!=a?await (0,l.teamListCall)(e,r?.organization_id||null,t):await (0,l.teamListCall)(e,r?.organization_id||null);e.s(["fetchTeams",0,n],270345);var o=e.i(243652),c=e.i(431703);let u=async(e,t,a,r={})=>{try{let i=(0,l.getProxyBaseUrl)(),s=new URLSearchParams(Object.entries({team_id:r.teamID,organization_id:r.organizationID,team_alias:r.team_alias,search:r.search,user_id:r.userID,page:t,page_size:a,sort_by:r.sortBy,sort_order:r.sortOrder,status:r.status}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),n=`${i?`${i}/v2/team/list`:"/v2/team/list"}?${s}`,o=await fetch(n,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok){let e=await o.json(),t=(0,c.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return await o.json()}catch(e){throw console.error("Failed to list teams:",e),e}},d=(0,o.createQueryKeys)("teams"),f=async e=>{let t=await u(e,1,100),a=t.total_pages??1;return a<=1?t.teams:[t,...await Promise.all(Array.from({length:a-1},(t,a)=>u(e,a+2,100)))].flatMap(e=>e.teams)},m=(0,o.createQueryKeys)("infiniteTeams"),h=async(e,t,a,r={})=>{try{let i=(0,l.getProxyBaseUrl)(),s=new URLSearchParams(Object.entries({team_id:r.teamID,organization_id:r.organizationID,team_alias:r.team_alias,search:r.search,user_id:r.userID,page:t,page_size:a,sort_by:r.sortBy,sort_order:r.sortOrder,status:"deleted"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),n=`${i?`${i}/v2/team/list`:"/v2/team/list"}?${s}`,o=await fetch(n,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok){let e=await o.json(),t=(0,c.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}let u=await o.json();if(u&&"object"==typeof u&&"teams"in u)return u.teams;return u}catch(e){throw console.error("Failed to list deleted teams:",e),e}},g=(0,o.createQueryKeys)("deletedTeams");e.s(["teamListCall",0,u,"useAllTeams",0,()=>{let{accessToken:e}=(0,s.default)();return(0,r.useQuery)({queryKey:d.list({filters:{scope:"all",pageSize:100,accessToken:e??""}}),queryFn:async()=>await f(e),enabled:!!e,staleTime:3e4})},"useDeletedTeams",0,(e,a,i={})=>{let{accessToken:l}=(0,s.default)();return(0,r.useQuery)({queryKey:g.list({page:e,limit:a,...i}),queryFn:async()=>await h(l,e,a,i),enabled:!!l,staleTime:3e4,placeholderData:t.keepPreviousData})},"useInfiniteTeams",0,(e=50,t,r)=>{let{accessToken:i,userId:l,userRole:n}=(0,s.default)(),o="Admin"===n||"Admin Viewer"===n;return(0,a.useInfiniteQuery)({queryKey:m.list({filters:{pageSize:e,...t&&{search:t},...r&&{organizationId:r},...l&&{userId:l}}}),queryFn:async({pageParam:a})=>await u(i,a,e,{team_alias:t||void 0,organizationID:r,userID:o?void 0:l}),initialPageParam:1,getNextPageParam:e=>{if(e.page{let{accessToken:t}=(0,s.default)(),a=(0,i.useQueryClient)();return(0,r.useQuery)({queryKey:d.detail(e),enabled:!!(t&&e),queryFn:async()=>{if(!t||!e)throw Error("Missing auth or teamId");return(0,l.teamInfoCall)(t,e)},initialData:()=>{if(!e)return;let t=a.getQueryData(d.list({}));return t?.find(t=>t.team_id===e)}})},"useTeams",0,()=>{let{accessToken:e,userId:t,userRole:a}=(0,s.default)();return(0,r.useQuery)({queryKey:d.list({}),queryFn:async()=>await n(e,t,a,null),enabled:!!e})}],785242)},109799,e=>{"use strict";var t=e.i(135214),a=e.i(602869),r=e.i(266027),i=e.i(912598);let s=(0,e.i(243652).createQueryKeys)("organizations");e.s(["organizationKeys",0,s,"useOrganization",0,e=>{let l=(0,i.useQueryClient)(),{accessToken:n}=(0,t.default)();return(0,r.useQuery)({queryKey:s.detail(e),enabled:!!(n&&e),queryFn:async()=>{if(!n||!e)throw Error("Missing auth or teamId");return(0,a.organizationInfoCall)(n,e)},initialData:()=>{if(e)return l.getQueriesData({queryKey:s.lists()}).flatMap(([,e])=>e??[]).find(t=>t.organization_id===e)}})},"useOrganizations",0,e=>{let{accessToken:i,userId:l,userRole:n}=(0,t.default)(),o=e?.org_id||null,c=e?.org_alias||null;return(0,r.useQuery)({queryKey:s.list(o||c?{filters:{...o&&{org_id:o},...c&&{org_alias:c}}}:{}),queryFn:async()=>await (0,a.organizationListCall)(i,o,c),enabled:!!(i&&l&&n)})}])},531278,e=>{"use strict";let t=(0,e.i(475254).default)("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);e.s(["Loader2",0,t],531278)},98740,e=>{"use strict";let t=(0,e.i(475254).default)("users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["path",{d:"M16 3.128a4 4 0 0 1 0 7.744",key:"16gr8j"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}]]);e.s(["default",0,t])},195116,e=>{"use strict";let t=(0,e.i(475254).default)("wrench",[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",key:"cbrjhi"}]]);e.s(["Wrench",0,t],195116)},657150,e=>{"use strict";let t=(0,e.i(475254).default)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);e.s(["default",0,t])},531245,e=>{"use strict";var t=e.i(657150);e.s(["Bot",()=>t.default])},755151,e=>{"use strict";var t=e.i(247153);e.s(["DownOutlined",()=>t.default])},326373,e=>{"use strict";var t=e.i(21539);e.s(["Dropdown",()=>t.default])},344523,e=>{"use strict";let t=(0,e.i(475254).default)("chevrons-up-down",[["path",{d:"m7 15 5 5 5-5",key:"1hf1tw"}],["path",{d:"m7 9 5-5 5 5",key:"sgt6xg"}]]);e.s(["ChevronsUpDown",0,t],344523)},100486,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M899.6 276.5L705 396.4 518.4 147.5a8.06 8.06 0 00-12.9 0L319 396.4 124.3 276.5c-5.7-3.5-13.1 1.2-12.2 7.9L188.5 865c1.1 7.9 7.9 14 16 14h615.1c8 0 14.9-6 15.9-14l76.4-580.6c.8-6.7-6.5-11.4-12.3-7.9zm-126 534.1H250.3l-53.8-409.4 139.8 86.1L512 252.9l175.7 234.4 139.8-86.1-53.9 409.4zM512 509c-62.1 0-112.6 50.5-112.6 112.6S449.9 734.2 512 734.2s112.6-50.5 112.6-112.6S574.1 509 512 509zm0 160.9c-26.6 0-48.2-21.6-48.2-48.3 0-26.6 21.6-48.3 48.2-48.3s48.2 21.6 48.2 48.3c0 26.6-21.6 48.3-48.2 48.3z"}}]},name:"crown",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["CrownOutlined",0,s],100486)},602073,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64L128 192v384c0 212.1 171.9 384 384 384s384-171.9 384-384V192L512 64zm312 512c0 172.3-139.7 312-312 312S200 748.3 200 576V246l312-110 312 110v330z"}},{tag:"path",attrs:{d:"M378.4 475.1a35.91 35.91 0 00-50.9 0 35.91 35.91 0 000 50.9l129.4 129.4 2.1 2.1a33.98 33.98 0 0048.1 0L730.6 434a33.98 33.98 0 000-48.1l-2.8-2.8a33.98 33.98 0 00-48.1 0L483 579.7 378.4 475.1z"}}]},name:"safety",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["SafetyOutlined",0,s],602073)},477189,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"}}]},name:"appstore",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["AppstoreOutlined",0,s],477189)},295320,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M704 446H320c-4.4 0-8 3.6-8 8v402c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8V454c0-4.4-3.6-8-8-8zm-328 64h272v117H376V510zm272 290H376V683h272v117z"}},{tag:"path",attrs:{d:"M424 748a32 32 0 1064 0 32 32 0 10-64 0zm0-178a32 32 0 1064 0 32 32 0 10-64 0z"}},{tag:"path",attrs:{d:"M811.4 368.9C765.6 248 648.9 162 512.2 162S258.8 247.9 213 368.8C126.9 391.5 63.5 470.2 64 563.6 64.6 668 145.6 752.9 247.6 762c4.7.4 8.7-3.3 8.7-8v-60.4c0-4-3-7.4-7-7.9-27-3.4-52.5-15.2-72.1-34.5-24-23.5-37.2-55.1-37.2-88.6 0-28 9.1-54.4 26.2-76.4 16.7-21.4 40.2-36.9 66.1-43.7l37.9-10 13.9-36.7c8.6-22.8 20.6-44.2 35.7-63.5 14.9-19.2 32.6-36 52.4-50 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.3c19.9 14 37.5 30.8 52.4 50 15.1 19.3 27.1 40.7 35.7 63.5l13.8 36.6 37.8 10c54.2 14.4 92.1 63.7 92.1 120 0 33.6-13.2 65.1-37.2 88.6-19.5 19.2-44.9 31.1-71.9 34.5-4 .5-6.9 3.9-6.9 7.9V754c0 4.7 4.1 8.4 8.8 8 101.7-9.2 182.5-94 183.2-198.2.6-93.4-62.7-172.1-148.6-194.9z"}}]},name:"cloud-server",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["CloudServerOutlined",0,s],295320)},283713,e=>{"use strict";var t=e.i(271645),a=e.i(602869),r=e.i(612256);let i="litellm_selected_worker_id";e.s(["useWorker",0,()=>{let{data:e}=(0,r.useUIConfig)(),s=e?.is_control_plane??!1,l=e?.workers??[],[n,o]=(0,t.useState)(()=>localStorage.getItem(i));(0,t.useEffect)(()=>{if(!n||0===l.length)return;let e=l.find(e=>e.worker_id===n);e&&(0,a.switchToWorkerUrl)(e.url)},[n,l]);let c=l.find(e=>e.worker_id===n)??null,u=(0,t.useCallback)(e=>{let t=l.find(t=>t.worker_id===e);t&&(o(e),localStorage.setItem(i,e),(0,a.switchToWorkerUrl)(t.url))},[l]);return{isControlPlane:s,workers:l,selectedWorkerId:n,selectedWorker:c,selectWorker:u,disconnectFromWorker:(0,t.useCallback)(()=>{o(null),localStorage.removeItem(i),(0,a.switchToWorkerUrl)(null)},[])}}])},463059,e=>{"use strict";var t=e.i(246349);e.s(["ChevronRight",()=>t.default])},951437,e=>{"use strict";var t=e.i(271645);e.s(["useControlled",0,function({controlled:e,default:a,name:r,state:i="value"}){let{current:s}=t.useRef(void 0!==e),[l,n]=t.useState(a),o=t.useCallback(e=>{s||n(e)},[]);return[s?e:l,o]}])},678745,e=>{"use strict";let t=(0,e.i(475254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,t])},54943,e=>{"use strict";let t=(0,e.i(475254).default)("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]);e.s(["default",0,t])},555436,e=>{"use strict";var t=e.i(54943);e.s(["Search",()=>t.default])},664659,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDown",()=>t.default])},643531,e=>{"use strict";var t=e.i(678745);e.s(["Check",()=>t.default])},652225,e=>{"use strict";var t=e.i(271645),a=e.i(552245);let r=t.forwardRef(function(e,t){let{className:r,render:i,orientation:s="horizontal",style:l,...n}=e;return(0,a.useRenderElement)("div",e,{state:{orientation:s},ref:t,props:[{role:"separator","aria-orientation":s},n]})});e.s(["Separator",0,r])},201675,e=>{"use strict";e.s(["clamp",0,function(e,t=Number.MIN_SAFE_INTEGER,a=Number.MAX_SAFE_INTEGER){return Math.max(t,Math.min(e,a))}])},346570,e=>{"use strict";var t=e.i(271645),a=e.i(174080),r=e.i(647554),i=e.i(383976),s=e.i(675606),l=e.i(56434);e.s(["useTriggerFocusGuards",0,function(e,n){let o=t.useRef(null);return{preFocusGuardRef:o,handlePreFocusGuardFocus:function(t){a.flushSync(()=>{e.setOpen(!1,(0,s.createChangeEventDetails)(l.REASONS.focusOut,t.nativeEvent,t.currentTarget))});let r=(0,i.getTabbableBeforeElement)(o.current);r?.focus()},handleFocusTargetFocus:function(t){let o=e.select("positionerElement");if(o&&(0,i.isOutsideEvent)(t,o))e.context.beforeContentFocusGuardRef.current?.focus();else{a.flushSync(()=>{e.setOpen(!1,(0,s.createChangeEventDetails)(l.REASONS.focusOut,t.nativeEvent,t.currentTarget))});let c=(0,i.getTabbableAfterElement)(e.context.triggerFocusTargetRef.current||n.current);for(;null!==c&&(0,r.contains)(o,c);){let e=c;if((c=(0,i.getNextTabbable)(c))===e)break}c?.focus()}}}}])},33383,96533,e=>{"use strict";var t=e.i(271645),a=e.i(108868),r=e.i(145484),i=e.i(146376);e.s(["useAnchoredPopupScrollLock",0,function(e,s,l,n){let[o,c]=t.useState(!1);(0,i.useIsoLayoutEffect)(()=>{if(!e||!s||null==l)return void c(!1);let t=(0,a.ownerDocument)(l).documentElement.clientWidth,r=l.offsetWidth;c(t>0&&r>0&&r>=t-20)},[e,s,l]),(0,r.useScrollLock)(e&&(!s||o),n)}],33383),e.i(247167);var s=e.i(733332);let l=t.createContext(void 0);e.s(["useToolbarRootContext",0,function(e){let a=t.useContext(l);if(void 0===a&&!e)throw Error((0,s.default)(69));return a}],96533)},469690,875812,381104,e=>{"use strict";e.i(247167);var t,a=e.i(733332),r=e.i(271645),i=e.i(956789);let s=((t={}).disabled="data-disabled",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),l={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},n={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},o={disabled:!1,...n};e.s(["DEFAULT_FIELD_ROOT_STATE",0,o,"DEFAULT_FIELD_STATE_ATTRIBUTES",0,n,"DEFAULT_VALIDITY_STATE",0,l,"fieldValidityMapping",0,{valid:e=>null===e?null:e?{[s.valid]:""}:{[s.invalid]:""}}],875812);let c={invalid:void 0,name:void 0,validityData:{state:l,errors:[],error:"",value:"",initialValue:null},setValidityData:i.NOOP,disabled:void 0,touched:n.touched,setTouched:i.NOOP,dirty:n.dirty,setDirty:i.NOOP,filled:n.filled,setFilled:i.NOOP,focused:n.focused,setFocused:i.NOOP,validate:()=>null,validationMode:"onSubmit",validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:o,markedDirtyRef:{current:!1},registerFieldControl:i.NOOP,validation:{getValidationProps:(e,t=i.EMPTY_OBJECT)=>t,inputRef:{current:null},registerInput:i.NOOP,commit:async()=>{},change:i.NOOP}},u=r.createContext(c);function d(e=!0){let t=r.useContext(u);if(t.setValidityData===i.NOOP&&!e)throw Error((0,a.default)(28));return t}e.s(["useFieldRootContext",0,d],469690);var f=e.i(146376);e.s(["useRegisterFieldControl",0,function(e,t,a,i,s=!0,l){let{registerFieldControl:n}=d(),o=r.useRef(null);o.current||(o.current=Symbol()),(0,f.useIsoLayoutEffect)(()=>{let r=o.current;if(r&&s)return n(r,{controlRef:e,getValue:i,id:t,name:l,value:a}),()=>{n(r,void 0)}},[e,s,i,t,l,n,a])}],381104)},884708,e=>{"use strict";var t=e.i(271645),a=e.i(956789);let r=t.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:a.NOOP,validationMode:"onSubmit",submitAttemptedRef:{current:!1}});e.s(["useFormContext",0,function(){return t.useContext(r)}])},538489,247778,e=>{"use strict";var t=e.i(271645),a=e.i(146376),r=e.i(667865),i=e.i(921374),s=e.i(229315),l=e.i(956789),n=e.i(788015);e.i(247167);let o=t.createContext({controlId:void 0,registerControlId:l.NOOP,labelId:void 0,setLabelId:l.NOOP,messageIds:[],setMessageIds:l.NOOP,getDescriptionProps:e=>e});function c(){return t.useContext(o)}e.s(["useLabelableContext",0,c],247778),e.s(["useLabelableId",0,function(e={}){let{id:o,implicit:u=!1,controlRef:d}=e,{controlId:f,registerControlId:m}=c(),h=(0,n.useBaseUiId)(o),g=u?f:void 0,p=(0,i.useRefWithInit)(()=>Symbol("labelable-control")),v=t.useRef(!1),y=t.useRef(null!=o),x=(0,r.useStableCallback)(()=>{v.current&&m!==l.NOOP&&(v.current=!1,m(p.current,void 0))});return(0,a.useIsoLayoutEffect)(()=>{let e;if(m!==l.NOOP){if(u){let t=d?.current;e=(0,s.isElement)(t)&&null!=t.closest("label")?o??null:g??h}else if(null!=o)y.current=!0,e=o;else{if(!y.current)return void x();e=h}if(void 0===e)return void x();v.current=!0,m(p.current,e)}},[o,d,g,m,u,h,p,x]),t.useEffect(()=>x,[x]),f??h}],538489)},757337,e=>{"use strict";var t=e.i(146376),a=e.i(788015);e.s(["useRegisteredLabelId",0,function(e,r){let i=(0,a.useBaseUiId)(e);return(0,t.useIsoLayoutEffect)(()=>(r(i),()=>{r(void 0)}),[i,r]),i}])},284614,e=>{"use strict";let t=(0,e.i(475254).default)("user",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]);e.s(["User",0,t],284614)},546467,e=>{"use strict";let t=(0,e.i(475254).default)("external-link",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);e.s(["default",0,t])},778917,e=>{"use strict";var t=e.i(546467);e.s(["ExternalLink",()=>t.default])},217923,e=>{"use strict";let t=(0,e.i(475254).default)("chart-column",[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}],["path",{d:"M18 17V9",key:"2bz60n"}],["path",{d:"M13 17V5",key:"1frdt8"}],["path",{d:"M8 17v-3",key:"17ska0"}]]);e.s(["BarChart3",0,t],217923)},686311,e=>{"use strict";let t=(0,e.i(475254).default)("message-square",[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z",key:"1lielz"}]]);e.s(["MessageSquare",0,t],686311)},465261,e=>{"use strict";let t=(0,e.i(475254).default)("key-round",[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]]);e.s(["KeyRound",0,t],465261)},772436,e=>{"use strict";var t=e.i(843476),a=e.i(652225),r=e.i(271645),i=e.i(115504);let s=r.forwardRef(({className:e,orientation:r="horizontal",...s},l)=>(0,t.jsx)(a.Separator,{ref:l,"data-slot":"separator",orientation:r,className:(0,i.cn)("shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",e),...s}));s.displayName="Separator",e.s(["Separator",0,s])},373264,e=>{"use strict";let t=(0,e.i(475254).default)("layout-grid",[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]]);e.s(["LayoutGrid",0,t],373264)},571303,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(115504);let i=a.default.forwardRef(({className:e="",...i},s)=>{var l,n;let o=(0,a.useId)();return l=()=>{let e=document.getAnimations().filter(e=>e instanceof CSSAnimation&&"spin"===e.animationName),t=e.find(e=>e.effect.target?.getAttribute("data-spinner-id")===o),a=e.find(e=>e.effect instanceof KeyframeEffect&&e.effect.target?.getAttribute("data-spinner-id")!==o);t&&a&&(t.currentTime=a.currentTime)},n=[o],(0,a.useLayoutEffect)(l,n),(0,t.jsxs)("svg",{ref:s,"data-spinner-id":o,className:(0,r.cx)("pointer-events-none size-12 animate-spin text-current",e),fill:"none",viewBox:"0 0 24 24",...i,children:[(0,t.jsx)("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),(0,t.jsx)("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]})});i.displayName="UiLoadingSpinner",e.s(["UiLoadingSpinner",0,i],571303)},936578,e=>{"use strict";var t=e.i(843476),a=e.i(115504),r=e.i(571303);e.s(["default",0,function(){return(0,t.jsxs)("div",{className:(0,a.cx)("h-screen","flex items-center justify-center gap-4"),children:[(0,t.jsx)("div",{className:"text-lg font-medium py-2 pr-4 border-r border-r-gray-200",children:"🚅 LiteLLM"}),(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2",children:[(0,t.jsx)(r.UiLoadingSpinner,{className:"size-4"}),(0,t.jsx)("span",{className:"text-gray-600 text-sm",children:"Loading..."})]})]})}])},953651,e=>{"use strict";let t=(0,e.i(475254).default)("server",[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]]);e.s(["default",0,t])},868054,e=>{"use strict";let t=(0,e.i(475254).default)("terminal",[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]]);e.s(["Terminal",0,t],868054)},844444,814431,e=>{"use strict";var t=e.i(843476),a=e.i(906579),r=e.i(271645),i=e.i(115571);function s(e){let t=t=>{"disableShowNewBadge"===t.key&&e()},a=t=>{let{key:a}=t.detail;"disableShowNewBadge"===a&&e()};return window.addEventListener("storage",t),window.addEventListener(i.LOCAL_STORAGE_EVENT,a),()=>{window.removeEventListener("storage",t),window.removeEventListener(i.LOCAL_STORAGE_EVENT,a)}}function l(){return"true"===(0,i.getLocalStorageItem)("disableShowNewBadge")}function n(){return(0,r.useSyncExternalStore)(s,l)}e.s(["useDisableShowNewBadge",0,n],814431),e.s(["default",0,function({children:e,dot:r=!1}){return n()?e?(0,t.jsx)(t.Fragment,{children:e}):null:e?(0,t.jsx)(a.Badge,{color:"blue",count:r?void 0:"New",dot:r,children:e}):(0,t.jsx)(a.Badge,{color:"blue",count:r?void 0:"New",dot:r})}],844444)},903446,e=>{"use strict";let t=(0,e.i(475254).default)("settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["default",0,t])},178583,38982,e=>{"use strict";var t=e.i(475254);let a=(0,t.default)("file-text",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]]);e.s(["FileText",0,a],178583);let r=(0,t.default)("flask-conical",[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2",key:"18mbvz"}],["path",{d:"M6.453 15h11.094",key:"3shlmq"}],["path",{d:"M8.5 2h7",key:"csnxdl"}]]);e.s(["FlaskConical",0,r],38982)},239616,e=>{"use strict";var t=e.i(903446);e.s(["Settings",()=>t.default])},98919,e=>{"use strict";let t=(0,e.i(475254).default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);e.s(["Shield",0,t],98919)},216370,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(519455),i=e.i(463059),s=e.i(115504);let l=a.forwardRef(({...e},a)=>(0,t.jsx)("nav",{ref:a,"aria-label":"breadcrumb","data-slot":"breadcrumb",...e}));l.displayName="Breadcrumb";let n=a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("ol",{ref:r,"data-slot":"breadcrumb-list",className:(0,s.cn)("flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground",e),...a}));n.displayName="BreadcrumbList";let o=a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("li",{ref:r,"data-slot":"breadcrumb-item",className:(0,s.cn)("inline-flex items-center gap-1.5",e),...a}));o.displayName="BreadcrumbItem",a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("a",{ref:r,"data-slot":"breadcrumb-link",className:(0,s.cn)("transition-colors hover:text-foreground",e),...a})).displayName="BreadcrumbLink";let c=a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("span",{ref:r,"data-slot":"breadcrumb-page",role:"link","aria-disabled":"true","aria-current":"page",className:(0,s.cn)("font-medium text-foreground",e),...a}));c.displayName="BreadcrumbPage";let u=a.forwardRef(({children:e,className:a,...r},l)=>(0,t.jsx)("li",{ref:l,"data-slot":"breadcrumb-separator",role:"presentation","aria-hidden":"true",className:(0,s.cn)("[&>svg]:size-3.5",a),...r,children:e??(0,t.jsx)(i.ChevronRight,{})}));u.displayName="BreadcrumbSeparator";var d=e.i(772436),f=e.i(111672),m=e.i(251773),h=e.i(771243),g=e.i(895335),p=e.i(853295),v=e.i(383862),y=e.i(283713),x=e.i(636772),b=e.i(268004),w=e.i(321836);function k({page:e}){let{title:a}=(0,f.getBreadcrumb)(e),{isControlPlane:i,selectedWorker:s}=(0,y.useWorker)(),_=(0,x.useDisableShowPrompts)();return(0,t.jsxs)("header",{className:"flex h-14 flex-none items-center justify-between gap-4 border-b border-border bg-background px-4",children:[(0,t.jsx)(l,{className:"min-w-0",children:(0,t.jsxs)(n,{className:"flex-nowrap",children:[(0,t.jsx)(o,{className:"flex-none",children:(0,t.jsx)(p.default,{})}),(0,t.jsx)(u,{}),(0,t.jsx)(o,{className:"min-w-0",children:(0,t.jsx)(c,{className:"truncate",children:a})})]})}),(0,t.jsxs)("div",{className:"flex flex-none items-center gap-1",children:[i&&null!==s&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(v.default,{onWorkerSwitch:e=>{(0,b.clearTokenCookies)(),(0,w.clearStoredReturnUrl)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=`/ui/login?worker=${encodeURIComponent(e)}`}}),(0,t.jsx)(d.Separator,{orientation:"vertical",className:"mx-1.5 h-5"})]}),(0,t.jsx)(r.Button,{variant:"ghost",size:"sm",nativeButton:!1,render:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/",target:"_blank",rel:"noopener noreferrer"}),className:"text-muted-foreground",children:"Docs"}),(0,t.jsx)(m.BlogDropdown,{}),!_&&(0,t.jsx)(h.CommunityEngagementButtons,{}),(0,t.jsx)(d.Separator,{orientation:"vertical",className:"mx-1.5 h-5"}),(0,t.jsx)(g.NotificationsBell,{})]})]})}var _=e.i(402874),j=e.i(936578),E=e.i(275144),S=e.i(557951),N=e.i(602869),C=e.i(135214);let M=({setPage:e,defaultSelectedKey:r,sidebarCollapsed:i,onToggleCollapsed:s})=>{let{accessToken:l}=(0,C.default)(),[n,o]=(0,a.useState)(null),[c,u]=(0,a.useState)(!1),[d,m]=(0,a.useState)(!1),[h,g]=(0,a.useState)(!1),[p,v]=(0,a.useState)(!1),[y,x]=(0,a.useState)(!1),[b,w]=(0,a.useState)(!1);return(0,a.useEffect)(()=>{(async()=>{if(l)try{let e=await (0,N.getUISettings)(l);e?.values?.enabled_ui_pages_internal_users!==void 0&&o(e.values.enabled_ui_pages_internal_users),e?.values?.enable_projects_ui!==void 0&&u(!!e.values.enable_projects_ui),e?.values?.enable_chat_ui!==void 0&&m(!!e.values.enable_chat_ui),e?.values?.disable_agents_for_internal_users!==void 0&&g(!!e.values.disable_agents_for_internal_users),e?.values?.allow_agents_for_team_admins!==void 0&&v(!!e.values.allow_agents_for_team_admins),e?.values?.disable_vector_stores_for_internal_users!==void 0&&x(!!e.values.disable_vector_stores_for_internal_users),e?.values?.allow_vector_stores_for_team_admins!==void 0&&w(!!e.values.allow_vector_stores_for_team_admins)}catch(e){console.error("[SidebarProvider] Failed to fetch UI settings:",e)}})()},[l]),(0,t.jsx)(f.default,{setPage:e,defaultSelectedKey:r,collapsed:i,onToggleCollapsed:s,enabledPagesInternalUsers:n,enableProjectsUI:c,enableChatUI:d,disableAgentsForInternalUsers:h,allowAgentsForTeamAdmins:p,disableVectorStoresForInternalUsers:y,allowVectorStoresForTeamAdmins:b})};var I=e.i(618566),P=e.i(560445),O=e.i(143488);let T=({accessToken:e})=>{let{data:a}=(0,O.useHealthReadinessDetails)(e);return a?.is_detailed_debug?(0,t.jsx)(P.Alert,{message:"Performance Warning: Detailed Debug Mode Active",description:(0,t.jsxs)(t.Fragment,{children:["Detailed debug logging (",(0,t.jsx)("code",{children:"LITELLM_LOG=DEBUG"}),") is currently enabled. This mode logs extensive diagnostic information and will significantly degrade performance. It should only be used for troubleshooting and disabled in production environments."]}),type:"warning",showIcon:!0,banner:!0,style:{marginBottom:0,borderRadius:0}}):null};var L=e.i(858488),R=e.i(625005);let z="sales@berri.ai",$=(0,t.jsx)("a",{href:`mailto:${z}`,children:z}),A=({licenseInfo:e})=>{let[r,i]=(0,a.useState)(!1),s=e?.expiration_date??null,l=(0,R.getLicenseExpiryTier)(s),n=(0,R.getDaysUntilExpiration)(s);if(null===s||"none"===l||null===n)return null;let o="warning"===l,c=`litellm:licenseExpiryBannerDismissed:${s}`,u=!!o&&"true"===sessionStorage.getItem(c);if(o&&(r||u))return null;let d=(0,R.formatExpiryDate)(s),f="expired"===l?`Your LiteLLM Enterprise license expired on ${d}`:`Your LiteLLM Enterprise license ${n<=0?"expires today":1===n?"expires in 1 day":`expires in ${n} days`} (${d})`,m="expired"===l?(0,t.jsxs)(t.Fragment,{children:["Enterprise features are now disabled. Reach out to ",$," to restore access"]}):"critical"===l?(0,t.jsxs)(t.Fragment,{children:["Renew now to avoid losing enterprise features. Reach out to ",$]}):(0,t.jsxs)(t.Fragment,{children:["Renew before it lapses to keep enterprise features. Reach out to ",$]});return(0,t.jsx)(P.Alert,{message:f,description:m,type:"warning"===l?"warning":"error",showIcon:!0,banner:!0,closable:o,onClose:()=>{sessionStorage.setItem(c,"true"),i(!0)},style:{marginBottom:0,borderRadius:0}})},D=({accessToken:e})=>{let{data:a}=(0,L.useLicenseInfo)(e);return(0,t.jsx)(A,{licenseInfo:a??null})};var B=e.i(571353),F=e.i(658140);let V=(0,e.i(431703).createApiClient)({getBaseUrl:()=>(0,N.getProxyBaseUrl)()??""});function H({children:e}){let{accessToken:a}=(0,S.useAuth)();return(0,t.jsx)(F.PluginModeProvider,{accessToken:a,children:e})}function U(){let{activePlugin:e}=(0,F.usePluginMode)(),r=e?.name,i=e?.url??"",{accessToken:s}=(0,S.useAuth)(),l=(0,a.useRef)(null),[n,o]=(0,a.useState)(null);return((0,a.useEffect)(()=>{if(!s||!r)return;let e=!1;return V.get("/api/plugins/auth-token",{accessToken:s,query:{plugin_name:r}}).then(t=>{!e&&t?.session_claim&&o({plugin:r,claim:t.session_claim})}).catch(()=>{}),()=>{e=!0}},[s,r]),(0,a.useEffect)(()=>{let e=l.current;if(!e||!n||n.plugin!==r||!i)return;let t=()=>{e.contentWindow?.postMessage({type:"litellm-auth",session_claim:n.claim},i)};return t(),e.addEventListener("load",t),()=>e.removeEventListener("load",t)},[n,r,i]),i)?(0,t.jsx)("iframe",{ref:l,src:`${i.replace(/\/$/,"")}/`,style:{width:"100%",height:"100%",border:"none",flex:1,minHeight:"calc(100vh - 56px)"},title:e?.display_name??"Plugin",allow:"clipboard-write"}):(0,t.jsx)("div",{className:"flex flex-1 items-center justify-center text-gray-500",children:(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsx)("p",{className:"text-lg font-medium mb-2",children:"Plugin"}),(0,t.jsx)("p",{className:"text-sm",children:"Configure the plugin URL in settings"})]})})}function q({children:e}){let r=(0,I.useRouter)(),i=(0,I.useSearchParams)(),s=(0,I.usePathname)(),{accessToken:l}=(0,S.useAuth)(),[n,o]=(0,a.useState)(!1),{mode:c}=(0,F.usePluginMode)(),u=(0,B.legacyKeyForPathname)(s)||i.get("page")||"api-keys";return"ai-gateway"!==c?(0,t.jsxs)("div",{className:"flex h-screen flex-col overflow-hidden bg-background",children:[(0,t.jsx)(_.default,{accessToken:l,isPublicPage:!1}),(0,t.jsx)(T,{accessToken:l}),(0,t.jsx)(D,{accessToken:l}),(0,t.jsx)("main",{className:"flex min-h-0 flex-1 overflow-hidden",children:(0,t.jsx)(U,{})})]}):(0,t.jsxs)("div",{className:"flex h-screen overflow-hidden bg-background",children:[(0,t.jsx)(M,{setPage:e=>{let t=B.MIGRATED_PAGES[e];r.push(t?(0,B.migratedHref)(t):(0,B.legacyPageHref)(e))},defaultSelectedKey:u,sidebarCollapsed:n,onToggleCollapsed:()=>o(e=>!e)}),(0,t.jsxs)("div",{className:"flex min-w-0 flex-1 flex-col overflow-hidden",children:[(0,t.jsx)(k,{page:u}),(0,t.jsx)(T,{accessToken:l}),(0,t.jsx)(D,{accessToken:l}),(0,t.jsx)("main",{className:"min-w-0 flex-1 overflow-y-auto",children:e})]})]})}function K({children:e}){let r=(0,I.useRouter)(),i=(0,I.useSearchParams)(),{accessToken:s,authLoading:l}=(0,S.useAuth)(),n=!!i.get("invitation_id");return((0,a.useEffect)(()=>{!l&&n&&r.replace(`${(0,B.migratedHref)("onboarding")}?${i.toString()}`)},[l,n,r,i]),l||n)?(0,t.jsx)(j.default,{}):(0,t.jsx)(E.ThemeProvider,{accessToken:s,children:(0,t.jsx)(q,{children:e})})}e.s(["AgentControlPlaneView",0,U,"default",0,function({children:e}){return(0,t.jsx)(a.Suspense,{fallback:(0,t.jsx)(j.default,{}),children:(0,t.jsx)(H,{children:(0,t.jsx)(K,{children:e})})})}],216370)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.cm9osit06~i.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.cm9osit06~i.js new file mode 100644 index 00000000000..565f5ec8246 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0.cm9osit06~i.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),s=e.i(529681);let l=e=>{let{prefixCls:a,className:s,style:l,size:n,shape:i}=e,o=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),c=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),d=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,o,c,s),style:Object.assign(Object.assign({},d),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),o=e.i(246422),c=e.i(838378);let d=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,i.unit)(e)}),u=e=>Object.assign({width:e},m(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),p=e=>Object.assign({width:e},m(e)),x=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),f=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:s,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:o,controlHeightLG:c,controlHeightSM:m,gradientFromColor:f,padding:b,marginSM:v,borderRadius:j,titleHeight:N,blockRadius:w,paragraphLiHeight:y,controlHeightXS:k,paragraphMarginTop:$}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:b,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},u(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},u(c)),[`${r}-sm`]:Object.assign({},u(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:N,background:f,borderRadius:w,[`+ ${s}`]:{marginBlockStart:m}},[s]:{padding:0,"> li":{width:"100%",height:y,listStyle:"none",background:f,borderRadius:w,"+ li":{marginBlockStart:k}}},[`${s}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${s} > li`]:{borderRadius:j}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:v,[`+ ${s}`]:{marginBlockStart:$}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},x(e,a,r)),{[`${r}-lg`]:Object.assign({},h(s,i))}),x(e,s,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),x(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},u(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},u(s)),[`${t}${t}-sm`]:Object.assign({},u(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(s,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:s,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:s},p(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},p(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${s} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),b=e=>{let{prefixCls:a,className:s,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,s),style:l},i)},v=({prefixCls:e,className:a,width:s,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:s},l)});function j(e){return e&&"object"==typeof e?e:{}}let N=e=>{let{prefixCls:s,loading:n,className:i,rootClassName:o,style:c,children:d,avatar:m=!1,title:u=!0,paragraph:g=!0,active:p,round:x}=e,{getPrefixCls:h,direction:N,className:w,style:y}=(0,a.useComponentConfig)("skeleton"),k=h("skeleton",s),[$,C,T]=f(k);if(n||!("loading"in e)){let e,a,s=!!m,n=!!u,d=!!g;if(s){let r=Object.assign(Object.assign({prefixCls:`${k}-avatar`},n&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),j(m));e=t.createElement("div",{className:`${k}-header`},t.createElement(l,Object.assign({},r)))}if(n||d){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${k}-title`},!s&&d?{width:"38%"}:s&&d?{width:"50%"}:{}),j(u));e=t.createElement(v,Object.assign({},r))}if(d){let e,a=Object.assign(Object.assign({prefixCls:`${k}-paragraph`},(e={},s&&n||(e.width="61%"),!s&&n?e.rows=3:e.rows=2,e)),j(g));r=t.createElement(b,Object.assign({},a))}a=t.createElement("div",{className:`${k}-content`},e,r)}let h=(0,r.default)(k,{[`${k}-with-avatar`]:s,[`${k}-active`]:p,[`${k}-rtl`]:"rtl"===N,[`${k}-round`]:x},w,i,o,C,T);return $(t.createElement("div",{className:h,style:Object.assign(Object.assign({},y),c)},e,a))}return null!=d?d:null};N.Button=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d=!1,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:m},b))))},N.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,shape:d="circle",size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls","className"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:d,size:m},b))))},N.Input=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:m},b))))},N.Image=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),d=c("skeleton",s),[m,u,g]=f(d),p=(0,r.default)(d,`${d}-element`,{[`${d}-active`]:o},l,n,u,g);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${d}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},N.Node=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o,children:c}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),m=d("skeleton",s),[u,g,p]=f(m),x=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:o},g,l,n,p);return u(t.createElement("div",{className:x},t.createElement("div",{className:(0,r.default)(`${m}-image`,l),style:i},c)))},e.s(["default",0,N],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let s=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(s),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let l=e<0?"-":"",n=Math.abs(e),i=n,o="";return n>=1e6?(i=n/1e6,o="M"):n>=1e3&&(i=n/1e3,o="K"),`${l}${i.toLocaleString("en-US",s)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),s=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(s.TooltipProvider,{delay:300,children:(0,t.jsxs)(s.Tooltip,{children:[(0,t.jsx)(s.TooltipTrigger,{render:r}),(0,t.jsx)(s.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:s,tooltip:i,dataTestId:o}){let c=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:s});return i?(0,t.jsx)(l,{content:i,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,o,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`,`${o}, ${c} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${a[d.getMonth()]} ${d.getDate()}, ${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:s,copyable:c=!1,truncate:d=!0,fallback:m="-",tooltip:u,disabled:g=!1,dataTestId:p,className:x}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let h=!!s&&!g,f=(0,n.cn)(o[a].base,h&&o[a].clickable,d&&"block max-w-[15ch] truncate",g&&"opacity-50",x),b=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":p,onClick:()=>s(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":p,children:e}),v=(0,r.jsx)(t.CellTooltip,{content:u??e,trigger:b});return c?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[v,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):v}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:s=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?s?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},o),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},o),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",i)},o),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},o),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},o),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("row"),i)},o),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),s=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let o={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,i.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:u,icon:g,size:p=s.Sizes.SM,tooltip:x,className:h,children:f}=e,b=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),v=g||null,{tooltipProps:j,getReferenceProps:N}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,n.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,n.tremorTwMerge)((0,i.getColorClassNames)(u,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(u,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(u,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),o[p].paddingX,o[p].paddingY,o[p].fontSize,h)},N,b),r.default.createElement(a.default,Object.assign({text:x},j)),v?r.default.createElement(v,{className:(0,n.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(d("text"),"whitespace-nowrap")},f))});m.displayName="Badge",e.s(["Badge",0,m],389083)},530212,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,r],530212)},384767,e=>{"use strict";var t=e.i(843476),r=e.i(599724),a=e.i(271645),s=e.i(389083);let l=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var n=e.i(602869);let i=function({vectorStores:e,accessToken:i}){let[o,c]=(0,a.useState)([]);return(0,a.useEffect)(()=>{(async()=>{if(i&&0!==e.length)try{let e=await (0,n.vectorStoreListCall)(i);e.data&&c(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[i,e.length]),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,t.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,r)=>{let a;return(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(a=o.find(t=>t.vector_store_id===e))?`${a.vector_store_name||a.vector_store_id} (${a.vector_store_id})`:e},r)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},o=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var c=e.i(871943),d=e.i(502547),m=e.i(592968),u=e.i(234713);let g=function({mcpServers:e,mcpAccessGroups:l=[],mcpToolPermissions:i={},mcpToolsets:g=[],accessToken:p}){let[x,h]=(0,a.useState)([]),[f,b]=(0,a.useState)([]),[v,j]=(0,a.useState)(new Set),[N,w]=(0,a.useState)(new Set);(0,a.useEffect)(()=>{(async()=>{if(p&&e.length>0)try{let e=await (0,n.fetchMCPServers)(p);e&&Array.isArray(e)?h(e):e.data&&Array.isArray(e.data)&&h(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[p,e.length]),(0,a.useEffect)(()=>{(async()=>{if(p&&g.length>0)try{let e=await (0,n.fetchMCPToolsets)(p),t=Array.isArray(e)?e.filter(e=>g.includes(e.toolset_id)):[];b(t)}catch(e){console.error("Error fetching toolsets:",e)}})()},[p,g.length]);let y=e.includes(u.NO_MCP_SERVERS_SENTINEL),k=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),$=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],C=$.length+g.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,t.jsx)(s.Badge,{color:y?"red":"blue",size:"xs",children:y?"Blocked":k?"All":C})]}),y?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-red-400"}),(0,t.jsx)(r.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):k?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-400"}),(0,t.jsx)(r.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):C>0?(0,t.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[$.map((e,r)=>{let a="server"===e.type?i[e.value]:void 0,s=a&&a.length>0,l=v.has(e.value);return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>{var t;return s&&(t=e.value,void j(e=>{let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=x.find(t=>t.server_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.alias} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a.length}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===a.length?"tool":"tools"}),l?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&l&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.map((e,r)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},r))})})]},r)}),g.length>0&&g.map((e,r)=>{let a=f.find(t=>t.toolset_id===e),s=N.has(e),l=a?.tools.length??0;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>l>0&&void w(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${l>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:a?.toolset_name??e}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),l>0&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:l}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===l?"tool":"tools"}),s?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),l>0&&s&&a&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.tools.map((e,r)=>(0,t.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,t.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},r))})})]},`toolset-${r}`)})]}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},p=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),x=function({agents:e,agentAccessGroups:l=[],accessToken:i}){let[o,c]=(0,a.useState)([]);(0,a.useEffect)(()=>{(async()=>{if(i&&e.length>0)try{let e=await (0,n.getAgentsList)(i);e&&e.agents&&Array.isArray(e.agents)&&c(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[i,e.length]);let d=[...e.map(e=>({type:"agent",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],u=d.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-purple-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,t.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,t.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:d.map((e,r)=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=o.find(t=>t.agent_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.agent_name} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},r))}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:a="card",className:s="",accessToken:l}){let n=e?.vector_stores||[],o=e?.mcp_servers||[],c=e?.mcp_access_groups||[],d=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],p=e?.agent_access_groups||[],h=e?.search_tools||[],f=(0,t.jsxs)("div",{className:"card"===a?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,t.jsx)(i,{vectorStores:n,accessToken:l}),(0,t.jsx)(g,{mcpServers:o,mcpAccessGroups:c,mcpToolPermissions:d,mcpToolsets:m,accessToken:l}),(0,t.jsx)(x,{agents:u,agentAccessGroups:p,accessToken:l}),(0,t.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,t.jsx)(r.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===h.length?(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-700",children:h.join(", ")})]})]});return"card"===a?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),f]}):(0,t.jsxs)("div",{className:`${s}`,children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),f]})}],384767)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.w8~sa9q0n_s.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.w8~sa9q0n_s.js deleted file mode 100644 index 6994bde5e6d..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0.w8~sa9q0n_s.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,207082,e=>{"use strict";var t=e.i(619273),s=e.i(266027),l=e.i(243652),a=e.i(602869),r=e.i(431703),i=e.i(135214);let n=(0,l.createQueryKeys)("keys"),o=async(e,t,s,l={})=>{try{let i=(0,a.getProxyBaseUrl)(),n=new URLSearchParams(Object.entries({team_id:l.teamID,project_id:l.projectID,agent_id:l.agentID,organization_id:l.organizationID,key_alias:l.selectedKeyAlias,key_hash:l.keyHash,user_id:l.userID,page:t,size:s,sort_by:l.sortBy,sort_order:l.sortOrder,expand:l.expand,status:l.status,return_full_object:"true",include_team_keys:"true",include_created_by_keys:"true",substring_matching:"true"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),o=`${i?`${i}/key/list`:"/key/list"}?${n}`,d=await fetch(o,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!d.ok){let e=await d.json(),t=(0,r.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return await d.json()}catch(e){throw console.error("Failed to list keys:",e),e}},d=(0,l.createQueryKeys)("deletedKeys");e.s(["keyKeys",0,n,"useDeletedKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:d.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,{...a,status:"deleted"}),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})},"useKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:n.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,a),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})}])},510674,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(431703),r=e.i(135214),i=e.i(708347);let n=(0,s.createQueryKeys)("projects"),o=[...i.all_admin_roles,...i.internalUserRoles],d=async e=>{let t=(0,l.getProxyBaseUrl)(),s=`${t}/project/list`,r=await fetch(s,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,a.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return r.json()};e.s(["projectKeys",0,n,"useProjects",0,()=>{let{accessToken:e,userRole:s}=(0,r.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>d(e),enabled:!!e&&o.includes(s)})}])},109034,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(135214);let r=(0,s.createQueryKeys)("tags");e.s(["useTags",0,()=>{let{accessToken:e,userId:s,userRole:i}=(0,a.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,l.tagListCall)(e),enabled:!!(e&&s&&i)})}])},552130,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select agents",disabled:d=!1})=>{let[c,u]=(0,s.useState)([]),[m,p]=(0,s.useState)([]),[g,h]=(0,s.useState)(!1);(0,s.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,a.getAgentsList)(n),t=e?.agents||[];u(t);let s=new Set;t.forEach(e=>{let t=e.agent_access_groups;t&&Array.isArray(t)&&t.forEach(e=>s.add(e))}),p(Array.from(s))}catch(e){console.error("Error fetching agents:",e)}finally{h(!1)}}})()},[n]);let x=[...m.map(e=>({label:e,value:`group:${e}`,isAccessGroup:!0,searchText:`${e} Access Group`})),...c.map(e=>({label:`${e.agent_name||e.agent_id}`,value:e.agent_id,isAccessGroup:!1,searchText:`${e.agent_name||e.agent_id} ${e.agent_id} Agent`}))],y=[...r?.agents||[],...(r?.accessGroups||[]).map(e=>`group:${e}`)];return(0,t.jsx)("div",{children:(0,t.jsx)(l.Select,{mode:"multiple",placeholder:o,onChange:t=>{e({agents:t.filter(e=>!e.startsWith("group:")),accessGroups:t.filter(e=>e.startsWith("group:")).map(e=>e.replace("group:",""))})},value:y,loading:g,className:i,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:d,filterOption:(e,t)=>(x.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:x.map(e=>(0,t.jsx)(l.Select.Option,{value:e.value,label:e.label,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:e.isAccessGroup?"#52c41a":"#722ed1",flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:e.isAccessGroup?"#52c41a":"#722ed1",fontSize:"12px",fontWeight:500,opacity:.8},children:e.isAccessGroup?"Access Group":"Agent"})]})},e.value))})})}])},557662,e=>{"use strict";let t="/ui/assets/logos/",s=[{id:"arize",displayName:"Arize",logo:`${t}arize.png`,supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_id:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:`${t}braintrust.png`,supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:`${t}custom.svg`,supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"galileo",displayName:"Galileo",logo:`${t}galileo.ico`,supports_key_team_logging:!1,dynamic_params:{GALILEO_API_KEY:"password",GALILEO_PROJECT_ID:"text",GALILEO_LOG_STREAM_ID:"text",GALILEO_BASE_URL:"text",GALILEO_USERNAME:"text",GALILEO_PASSWORD:"password"},description:"Galileo AI Observability Integration"},{id:"datadog",displayName:"Datadog",logo:`${t}datadog.png`,supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:`${t}lago.svg`,supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:`${t}langsmith.png`,supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:`${t}openmeter.png`,supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:`${t}otel.png`,supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"},{id:"SQS",displayName:"SQS",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{sqs_queue_url:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"SQS Queue (AWS) Logging Integration"}],l=s.reduce((e,t)=>(e[t.displayName]=t,e),{}),a=s.reduce((e,t)=>(e[t.displayName]=t.id,e),{}),r=s.reduce((e,t)=>(e[t.id]=t.displayName,e),{});e.s(["callbackInfo",0,l,"callback_map",0,a,"mapDisplayToInternalNames",0,e=>e.map(e=>a[e]||e),"mapInternalToDisplayNames",0,e=>e.map(e=>r[e]||e),"reverse_callback_map",0,r])},9314,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(981339),a=e.i(645526),r=e.i(599724),i=e.i(263147);e.s(["default",0,({value:e,onChange:n,placeholder:o="Select access groups",disabled:d=!1,style:c,className:u,showLabel:m=!1,labelText:p="Access Group",allowClear:g=!0})=>{let{data:h,isLoading:x,isError:y}=(0,i.useAccessGroups)();if(x)return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(l.Skeleton.Input,{active:!0,block:!0,style:{height:32,...c}})]});let f=(h??[]).map(e=>({label:(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"font-medium",children:e.access_group_name})," ",(0,t.jsxs)("span",{className:"text-gray-400 text-xs",children:["(",e.access_group_id,")"]})]}),value:e.access_group_id,selectedLabel:e.access_group_name,searchText:`${e.access_group_name} ${e.access_group_id}`}));return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(s.Select,{mode:"multiple",value:e,placeholder:o,onChange:n,disabled:d,allowClear:g,showSearch:!0,style:{width:"100%",...c},className:`rounded-md ${u??""}`,notFoundContent:y?(0,t.jsx)("span",{className:"text-red-500",children:"Failed to load access groups"}):"No access groups found",filterOption:(e,t)=>(f.find(e=>e.value===t?.value)?.searchText??"").toLowerCase().includes(e.toLowerCase()),optionLabelProp:"selectedLabel",options:f.map(e=>({label:e.label,value:e.value,selectedLabel:e.selectedLabel}))})]})}])},392110,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(592968),r=e.i(312361),i=e.i(790848),n=e.i(536916),o=e.i(827252),d=e.i(779241);let{Option:c}=l.Select;e.s(["default",0,({form:e,autoRotationEnabled:u,onAutoRotationChange:m,rotationInterval:p,onRotationIntervalChange:g,isCreateMode:h=!1,neverExpire:x=!1,onNeverExpireChange:y})=>{let f=p&&!["7d","30d","90d","180d","365d"].includes(p),[b,_]=(0,s.useState)(f),[j,v]=(0,s.useState)(f?p:""),[w,N]=(0,s.useState)(e?.getFieldValue?.("duration")||"");return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Key Expiry Settings"}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Expire Key"}),(0,t.jsx)(a.Tooltip,{title:"Set when this key should expire. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days). Leave empty to keep the current expiry unchanged.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),!h&&y&&(0,t.jsx)(n.Checkbox,{checked:x,onChange:t=>{let s=t.target.checked;y(s),s&&(N(""),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",""):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:""}))},className:"ml-2 text-sm font-normal text-gray-600",children:"Never Expire"})]}),(0,t.jsx)(d.TextInput,{name:"duration",placeholder:h?"e.g., 30d or leave empty to never expire":"e.g., 30d",className:"w-full",value:w,onValueChange:t=>{N(t),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",t):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:t})},disabled:!h&&x})]})]}),(0,t.jsx)(r.Divider,{}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Auto-Rotation Settings"}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Enable Auto-Rotation"}),(0,t.jsx)(a.Tooltip,{title:"Key will automatically regenerate at the specified interval for enhanced security.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsx)(i.Switch,{checked:u,onChange:m,size:"default",className:u?"":"bg-gray-400"})]}),u&&(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Rotation Interval"}),(0,t.jsx)(a.Tooltip,{title:"How often the key should be automatically rotated. Choose the interval that best fits your security requirements.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)(l.Select,{value:b?"custom":p,onChange:e=>{"custom"===e?_(!0):(_(!1),v(""),g(e))},className:"w-full",placeholder:"Select interval",children:[(0,t.jsx)(c,{value:"7d",children:"7 days"}),(0,t.jsx)(c,{value:"30d",children:"30 days"}),(0,t.jsx)(c,{value:"90d",children:"90 days"}),(0,t.jsx)(c,{value:"180d",children:"180 days"}),(0,t.jsx)(c,{value:"365d",children:"365 days"}),(0,t.jsx)(c,{value:"custom",children:"Custom interval"})]}),b&&(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(d.TextInput,{value:j,onChange:e=>{let t=e.target.value;v(t),g(t)},placeholder:"e.g., 1s, 5m, 2h, 14d"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Supported formats: seconds (s), minutes (m), hours (h), days (d)"})]})]})]})]}),u&&(0,t.jsx)("div",{className:"bg-blue-50 p-3 rounded-md text-sm text-blue-700",children:"When rotation occurs, you'll receive a notification with the new key. The old key will be deactivated after a brief grace period."})]})]})}])},533882,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(250980),a=e.i(797672),r=e.i(68155),i=e.i(304967),n=e.i(629569),o=e.i(599724),d=e.i(269200),c=e.i(427612),u=e.i(64848),m=e.i(942232),p=e.i(496020),g=e.i(977572),h=e.i(992619),x=e.i(727749);e.s(["default",0,({accessToken:e,initialModelAliases:y={},onAliasUpdate:f,showExampleConfig:b=!0})=>{let[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)({aliasName:"",targetModel:""}),[N,k]=(0,s.useState)(null);(0,s.useEffect)(()=>{j(Object.entries(y).map(([e,t],s)=>({id:`${s}-${e}`,aliasName:e,targetModel:t})))},[y]);let S=()=>{if(!N)return;if(!N.aliasName||!N.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.id!==N.id&&e.aliasName===N.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=_.map(e=>e.id===N.id?N:e);j(e),k(null);let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias updated successfully")},C=()=>{k(null)},T=_.reduce((e,t)=>(e[t.aliasName]=t.targetModel,e),{});return(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:v.aliasName,onChange:e=>w({...v,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model"}),(0,t.jsx)(h.default,{accessToken:e,value:v.targetModel,placeholder:"Select target model",onChange:e=>w({...v,targetModel:e}),showLabel:!1})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:()=>{if(!v.aliasName||!v.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.aliasName===v.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=[..._,{id:`${Date.now()}-${v.aliasName}`,aliasName:v.aliasName,targetModel:v.targetModel}];j(e),w({aliasName:"",targetModel:""});let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias added successfully")},disabled:!v.aliasName||!v.targetModel,className:`flex items-center px-4 py-2 rounded-md text-sm ${!v.aliasName||!v.targetModel?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(l.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(d.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(c.TableHead,{children:(0,t.jsxs)(p.TableRow,{children:[(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Target Model"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(m.TableBody,{children:[_.map(s=>(0,t.jsx)(p.TableRow,{className:"h-8",children:N&&N.id===s.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:N.aliasName,onChange:e=>k({...N,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)(h.default,{accessToken:e,value:N.targetModel,onChange:e=>k({...N,targetModel:e}),showLabel:!1,style:{height:"32px"}})}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:S,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:C,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-900",children:s.aliasName}),(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-500",children:s.targetModel}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{k({...s})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(a.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>{var e;let t,l;return e=s.id,j(t=_.filter(t=>t.id!==e)),l={},void(t.forEach(e=>{l[e.aliasName]=e.targetModel}),f&&f(l),x.default.success("Alias deleted successfully"))},className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(r.TrashIcon,{className:"w-3 h-3"})})]})})]})},s.id)),0===_.length&&(0,t.jsx)(p.TableRow,{children:(0,t.jsx)(g.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),b&&(0,t.jsxs)(i.Card,{children:[(0,t.jsx)(n.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(o.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["model_aliases:",0===Object.keys(T).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"  # No aliases configured yet"]}):Object.entries(T).map(([e,s])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'  "',e,'": "',s,'"']},e))]})})]})]})}])},844565,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select pass through routes",disabled:d=!1,teamId:c})=>{let[u,m]=(0,s.useState)([]),[p,g]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.getPassThroughEndpointsCall)(n,c);if(e.endpoints){let t=e.endpoints.flatMap(e=>{let t=e.path,s=e.methods;return s&&s.length>0?s.map(e=>({label:`${e} ${t}`,value:t})):[{label:t,value:t}]});m(t)}}catch(e){console.error("Error fetching pass through routes:",e)}finally{g(!1)}}})()},[n,c]),(0,t.jsx)(l.Select,{mode:"tags",placeholder:o,onChange:e,value:r,loading:p,className:i,allowClear:!0,options:u,optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})}])},810757,477386,e=>{"use strict";var t=e.i(271645);let s=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}))});e.s(["CogIcon",0,s],810757);let l=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"}))});e.s(["BanIcon",0,l],477386)},266484,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(592968),a=e.i(312361),r=e.i(827252),i=e.i(994388),n=e.i(304967),o=e.i(779241),d=e.i(988297),c=e.i(68155),u=e.i(810757),m=e.i(477386),p=e.i(557662),g=e.i(555987),h=e.i(435451);let{Option:x}=s.Select;e.s(["default",0,({value:e=[],onChange:y,disabledCallbacks:f=[],onDisabledCallbacksChange:b})=>{let _=Object.entries(p.callbackInfo).filter(([e,t])=>t.supports_key_team_logging).map(([e,t])=>e),j=Object.keys(p.callbackInfo),v=e=>{y?.(e)},w=(t,s,l)=>{let a=[...e];if("callback_name"===s){let e=p.callback_map[l]||l;a[t]={...a[t],[s]:e,callback_vars:{}}}else a[t]={...a[t],[s]:l};v(a)},N=(t,s,l)=>{let a=[...e];a[t]={...a[t],callback_vars:{...a[t].callback_vars,[s]:l}},v(a)};return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(m.BanIcon,{className:"w-5 h-5 text-red-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Disabled Callbacks"}),(0,t.jsx)(l.Tooltip,{title:"Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Disabled Callbacks"}),(0,t.jsx)(s.Select,{mode:"multiple",placeholder:"Select callbacks to disable",value:f,onChange:e=>{let t=(0,p.mapDisplayToInternalNames)(e);b?.(t)},style:{width:"100%"},optionLabelProp:"label",children:j.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Select callbacks that should be disabled for this key. These callbacks will not receive any logging data."})]})]}),(0,t.jsx)(a.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(u.CogIcon,{className:"w-5 h-5 text-blue-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Logging Integrations"}),(0,t.jsx)(l.Tooltip,{title:"Configure callback logging integrations for this team.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsx)(i.Button,{variant:"secondary",onClick:()=>{v([...e,{callback_name:"",callback_type:"success",callback_vars:{}}])},icon:d.PlusIcon,size:"sm",className:"hover:border-blue-400 hover:text-blue-500",type:"button",children:"Add Integration"})]}),(0,t.jsx)("div",{className:"space-y-4",children:e.map((a,d)=>{let u=a.callback_name?Object.entries(p.callback_map).find(([e,t])=>t===a.callback_name)?.[0]:void 0,m=u?(0,g.resolveLogoSrc)(p.callbackInfo[u]?.logo):null;return(0,t.jsxs)(n.Card,{className:"border border-gray-200 shadow-xs hover:shadow-md transition-shadow duration-200",decoration:"top",decorationColor:"blue",children:[(0,t.jsxs)("div",{className:"flex justify-between items-start mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[m&&(0,t.jsx)("img",{src:m,alt:u,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("span",{className:"text-sm font-medium",children:[u||"New Integration"," Configuration"]})]}),(0,t.jsx)(i.Button,{variant:"light",onClick:()=>{v(e.filter((e,t)=>t!==d))},icon:c.TrashIcon,size:"xs",color:"red",className:"hover:bg-red-50",type:"button",children:"Remove"})]}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Integration Type"}),(0,t.jsx)(s.Select,{value:u,placeholder:"Select integration",onChange:e=>w(d,"callback_name",e),className:"w-full",optionLabelProp:"label",children:_.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Event Type"}),(0,t.jsxs)(s.Select,{value:a.callback_type,onChange:e=>w(d,"callback_type",e),className:"w-full",children:[(0,t.jsx)(x,{value:"success",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),(0,t.jsx)("span",{children:"Success Only"})]})}),(0,t.jsx)(x,{value:"failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-red-500 rounded-full"}),(0,t.jsx)("span",{children:"Failure Only"})]})}),(0,t.jsx)(x,{value:"success_and_failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),(0,t.jsx)("span",{children:"Success & Failure"})]})})]})]})]}),((e,s)=>{if(!e.callback_name)return null;let a=Object.entries(p.callback_map).find(([t,s])=>s===e.callback_name)?.[0];if(!a)return null;let i=p.callbackInfo[a]?.dynamic_params||{};return 0===Object.keys(i).length?null:(0,t.jsxs)("div",{className:"mt-6 pt-4 border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2 mb-4",children:[(0,t.jsx)("div",{className:"w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center",children:(0,t.jsx)("div",{className:"w-1.5 h-1.5 bg-blue-500 rounded-full"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Integration Parameters"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-4",children:Object.entries(i).map(([a,i])=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 capitalize flex items-center space-x-1",children:[(0,t.jsx)("span",{children:a.replace(/_/g," ")}),(0,t.jsx)(l.Tooltip,{title:`Environment variable reference recommended: os.environ/${a.toUpperCase()}`,children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),"password"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Sensitive"}),"number"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Number"})]}),"number"===i&&(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Value must be between 0 and 1"}),"number"===i?(0,t.jsx)(h.default,{step:.01,width:400,placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)}):(0,t.jsx)(o.TextInput,{type:"password"===i?"password":"text",placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)})]},a))})]})})(a,d)]})]},d)})}),0===e.length&&(0,t.jsxs)("div",{className:"text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50",children:[(0,t.jsx)(u.CogIcon,{className:"w-12 h-12 text-gray-300 mb-3 mx-auto"}),(0,t.jsx)("div",{className:"text-base font-medium mb-1",children:"No logging integrations configured"}),(0,t.jsx)("div",{className:"text-sm text-gray-400",children:'Click "Add Integration" to configure logging for this team'})]})]})}])},651904,e=>{"use strict";var t=e.i(843476),s=e.i(599724),l=e.i(266484);e.s(["default",0,function({value:e,onChange:a,premiumUser:r=!1,disabledCallbacks:i=[],onDisabledCallbacksChange:n}){return r?(0,t.jsx)(l.default,{value:e,onChange:a,disabledCallbacks:i,onDisabledCallbacksChange:n}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex flex-wrap gap-2 mb-3",children:[(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ langfuse-logging"}),(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ datadog-logging"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(s.Text,{className:"text-sm text-yellow-800",children:["Setting Key/Team logging settings is a LiteLLM Enterprise feature. Global Logging Settings are available for all free users. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})}])},939510,e=>{"use strict";var t=e.i(843476),s=e.i(808613),l=e.i(199133),a=e.i(592968),r=e.i(827252);let{Option:i}=l.Select;e.s(["default",0,({type:e,name:n,showDetailedDescriptions:o=!0,className:d="",initialValue:c=null,form:u,onChange:m})=>{let p=e.toUpperCase(),g=e.toLowerCase(),h=`Select 'guaranteed_throughput' to prevent overallocating ${p} limit when the key belongs to a Team with specific ${p} limits.`;return(0,t.jsx)(s.Form.Item,{label:(0,t.jsxs)("span",{children:[p," Rate Limit Type"," ",(0,t.jsx)(a.Tooltip,{title:h,children:(0,t.jsx)(r.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:n,initialValue:c,className:d,children:(0,t.jsx)(l.Select,{defaultValue:o?"default":void 0,placeholder:"Select rate limit type",style:{width:"100%"},optionLabelProp:o?"label":void 0,onChange:e=>{u&&u.setFieldValue(n,e),m&&m(e)},children:o?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",label:"Default",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Default"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Best effort throughput - no error if we're overallocating ",g," (Team/Key Limits checked at runtime)."]})]})}),(0,t.jsx)(i,{value:"guaranteed_throughput",label:"Guaranteed throughput",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Guaranteed throughput"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Guaranteed throughput - raise an error if we're overallocating ",g," (also checks model-specific limits)"]})]})}),(0,t.jsx)(i,{value:"dynamic",label:"Dynamic",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Dynamic"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["If the key has a set ",p," (e.g. 2 ",p,") and there are no 429 errors, it can dynamically exceed the limit when the model being called is not erroring."]})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",children:"Best effort throughput"}),(0,t.jsx)(i,{value:"guaranteed_throughput",children:"Guaranteed throughput"}),(0,t.jsx)(i,{value:"dynamic",children:"Dynamic"})]})})})}])},460285,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(404206),a=e.i(723731),r=e.i(653824),i=e.i(881073),n=e.i(197647),o=e.i(602869),d=e.i(158392),c=e.i(419470),u=e.i(695411);let m=(0,s.forwardRef)(({accessToken:e,value:m,onChange:p,modelData:g},h)=>{let[x,y]=(0,s.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[f,b]=(0,s.useState)([]),[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)([]),[N,k]=(0,s.useState)([]),[S,C]=(0,s.useState)({}),[T,I]=(0,s.useState)({}),A=(0,s.useRef)(!1),L=(0,s.useRef)(null);(0,s.useEffect)(()=>{let e=m?.router_settings?JSON.stringify({routing_strategy:m.router_settings.routing_strategy,fallbacks:m.router_settings.fallbacks,enable_tag_filtering:m.router_settings.enable_tag_filtering}):null;if(A.current&&e===L.current){A.current=!1;return}if(A.current&&e!==L.current&&(A.current=!1),e!==L.current)if(L.current=e,m?.router_settings){let e=m.router_settings,{fallbacks:t,...s}=e;y({routerSettings:s,selectedStrategy:e.routing_strategy||null,enableTagFiltering:e.enable_tag_filtering??!1});let l=e.fallbacks||[];b(l),j(l&&0!==l.length?l.map((e,t)=>{let[s,l]=Object.entries(e)[0];return{id:(t+1).toString(),primaryModel:s||null,fallbackModels:l||[]}}):[{id:"1",primaryModel:null,fallbackModels:[]}])}else y({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),b([]),j([{id:"1",primaryModel:null,fallbackModels:[]}])},[m]),(0,s.useEffect)(()=>{e&&(0,o.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),C(t);let s=e.fields.find(e=>"routing_strategy"===e.field_name);s?.options&&k(s.options),e.routing_strategy_descriptions&&I(e.routing_strategy_descriptions)}})},[e]),(0,s.useEffect)(()=>{e&&(async()=>{try{let t=await (0,u.fetchAvailableModels)(e);w(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}})()},[e]);let F=()=>{let e=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),t=new Set(["model_group_alias","retry_policy"]),s=Object.fromEntries(Object.entries({...x.routerSettings,enable_tag_filtering:x.enableTagFiltering,routing_strategy:x.selectedStrategy,fallbacks:f.length>0?f:null}).map(([s,l])=>{if("routing_strategy_args"!==s&&"routing_strategy"!==s&&"enable_tag_filtering"!==s&&"fallbacks"!==s){let a=document.querySelector(`input[name="${s}"]`);if(a){if(void 0!==a.value&&""!==a.value){let r=((s,l,a)=>{if(null==l)return a;let r=String(l).trim();if(""===r||"null"===r.toLowerCase())return null;if(e.has(s)){let e=Number(r);return Number.isNaN(e)?a:e}if(t.has(s)){if(""===r)return null;try{return JSON.parse(r)}catch{return a}}return"true"===r.toLowerCase()||"false"!==r.toLowerCase()&&r})(s,a.value,l);return[s,r]}return[s,null]}}else if("routing_strategy"===s)return[s,x.selectedStrategy];else if("enable_tag_filtering"===s)return[s,x.enableTagFiltering];else if("fallbacks"===s)return[s,f.length>0?f:null];else if("routing_strategy_args"===s&&"latency-based-routing"===x.selectedStrategy){let e=document.querySelector('input[name="lowest_latency_buffer"]'),t=document.querySelector('input[name="ttl"]'),s={};return e?.value&&(s.lowest_latency_buffer=Number(e.value)),t?.value&&(s.ttl=Number(t.value)),["routing_strategy_args",Object.keys(s).length>0?s:null]}return[s,l]}).filter(e=>null!=e)),l=(e,t=!1)=>null==e||"object"==typeof e&&!Array.isArray(e)&&0===Object.keys(e).length||t&&("number"!=typeof e||Number.isNaN(e))?null:e;return{routing_strategy:l(s.routing_strategy),allowed_fails:l(s.allowed_fails,!0),cooldown_time:l(s.cooldown_time,!0),num_retries:l(s.num_retries,!0),timeout:l(s.timeout,!0),retry_after:l(s.retry_after,!0),fallbacks:f.length>0?f:null,context_window_fallbacks:l(s.context_window_fallbacks),retry_policy:l(s.retry_policy),model_group_alias:l(s.model_group_alias),enable_tag_filtering:x.enableTagFiltering,routing_strategy_args:l(s.routing_strategy_args)}};(0,s.useEffect)(()=>{if(!p)return;let e=setTimeout(()=>{A.current=!0,p({router_settings:F()})},100);return()=>clearTimeout(e)},[x,f]);let M=Array.from(new Set(v.map(e=>e.model_group))).sort();return((0,s.useImperativeHandle)(h,()=>({getValue:()=>({router_settings:F()})})),e)?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(r.TabGroup,{className:"w-full",children:[(0,t.jsxs)(i.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(n.Tab,{value:"2",children:"Fallbacks"})]}),(0,t.jsxs)(a.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(d.default,{value:x,onChange:y,routerFieldsMetadata:S,availableRoutingStrategies:N,routingStrategyDescriptions:T})}),(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(c.FallbackSelectionForm,{groups:_,onGroupsChange:e=>{j(e),b(e.filter(e=>e.primaryModel&&e.fallbackModels.length>0).map(e=>({[e.primaryModel]:e.fallbackModels})))},availableModels:M,maxGroups:5})})]})]})}):null});m.displayName="RouterSettingsAccordion",e.s(["default",0,m])},363256,e=>{"use strict";var t=e.i(843476),s=e.i(199133);let{Text:l}=e.i(898586).Typography;e.s(["default",0,({organizations:e,value:a,onChange:r,disabled:i,loading:n,style:o})=>(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"All Organizations",value:a,onChange:r,disabled:i,loading:n,allowClear:!0,style:{minWidth:280,...o},filterOption:(t,s)=>{if(!s)return!1;let l=e?.find(e=>e.organization_id===s.key);if(!l)return!1;let a=t.toLowerCase().trim(),r=(l.organization_alias||"").toLowerCase(),i=(l.organization_id||"").toLowerCase();return r.includes(a)||i.includes(a)},children:e?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.organization_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.organization_alias})," ",(0,t.jsxs)(l,{type:"secondary",children:["(",e.organization_id,")"]})]},e.organization_id))})])},575260,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(482725),a=e.i(56456);e.s(["default",0,({projects:e,value:r,onChange:i,disabled:n,loading:o,teamId:d})=>{let c=d?e?.filter(e=>e.team_id===d):e;return(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"Search or select a project",value:r,onChange:i,disabled:n,loading:o,allowClear:!0,notFoundContent:o?(0,t.jsx)(l.Spin,{indicator:(0,t.jsx)(a.LoadingOutlined,{spin:!0}),size:"small"}):void 0,filterOption:(e,t)=>{if(!t)return!1;let s=c?.find(e=>e.project_id===t.key);if(!s)return!1;let l=e.toLowerCase().trim(),a=(s.project_alias||"").toLowerCase(),r=(s.project_id||"").toLowerCase();return a.includes(l)||r.includes(l)},optionFilterProp:"children",children:!o&&c?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.project_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.project_alias||e.project_id})," ",(0,t.jsxs)("span",{className:"text-gray-500",children:["(",e.project_id,")"]})]},e.project_id))})}])},128233,319312,e=>{"use strict";var t=e.i(843476),s=e.i(464571),l=e.i(199133),a=e.i(592968),r=e.i(425063),i=e.i(107233),n=e.i(37727),o=e.i(271645);e.s(["BudgetFallbacksEditor",0,function({value:e,onChange:d,availableModels:c}){let[u,m]=(0,o.useState)(()=>{let t;return 0===(t=Object.keys(e)).length?[]:t.map((t,s)=>({id:String(s+1),primaryModel:t,fallbackModels:e[t]}))}),p=e=>{m(e),d(Object.fromEntries(e.filter(e=>null!==e.primaryModel&&e.fallbackModels.length>0).map(e=>[e.primaryModel,e.fallbackModels])))},g=()=>{p([...u,{id:Date.now().toString(),primaryModel:null,fallbackModels:[]}])},h=(e,t)=>{p(u.map(s=>s.id===e?{...s,...t}:s))},x=new Set(u.map(e=>e.primaryModel).filter(Boolean));return 0===u.length?(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-2",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),u.map(e=>{let s=c.filter(t=>t===e.primaryModel||!x.has(t)),i=c.filter(t=>t!==e.primaryModel);return(0,t.jsxs)("div",{className:"relative rounded-lg border border-gray-200 bg-gray-50 p-4",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{var t;return t=e.id,void p(u.filter(e=>e.id!==t))},className:"absolute top-2 right-2 text-gray-400 hover:text-red-500 transition-colors p-1",children:(0,t.jsx)(n.X,{className:"w-4 h-4"})}),(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Primary Model"}),(0,t.jsx)(l.Select,{className:"w-full",placeholder:"Select model",value:e.primaryModel,onChange:t=>{let s=e.fallbackModels.filter(e=>e!==t);h(e.id,{primaryModel:t,fallbackModels:s})},showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:s.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-1 mb-2",children:(0,t.jsxs)("div",{className:"bg-amber-50 text-amber-600 px-3 py-0.5 rounded-full text-[10px] font-bold border border-amber-100 flex items-center gap-1",children:[(0,t.jsx)(r.ArrowDown,{className:"w-3 h-3"}),"IF BUDGET EXCEEDED, TRY"]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Fallback Models"}),(0,t.jsx)(l.Select,{mode:"multiple",className:"w-full",placeholder:e.primaryModel?"Select fallback models":"Select a primary model first",value:e.fallbackModels,onChange:t=>h(e.id,{fallbackModels:t}),disabled:!e.primaryModel,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:i.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(a.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})}),e.fallbackModels.length>1&&(0,t.jsx)("div",{className:"text-[10px] text-gray-400 mt-1 ml-1",children:"Tried in order; first model still within its own budget is used"})]})]},e.id)}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]})}],128233);var d=e.i(28651);let c=[{value:"1h",label:"Hourly",resetHint:"Resets every hour"},{value:"24h",label:"Daily",resetHint:"Resets daily at midnight UTC"},{value:"7d",label:"Weekly",resetHint:"Resets every Sunday at midnight UTC"},{value:"30d",label:"Monthly",resetHint:"Resets on the 1st of every month at midnight UTC"}];e.s(["BudgetWindowsEditor",0,function({value:e,onChange:a}){let r=(t,s,l)=>{a(e.map((e,a)=>a===t?{...e,[s]:l}:e))};return(0,t.jsxs)("div",{children:[e.map((i,n)=>{let o=c.find(e=>e.value===i.budget_duration)?.resetHint;return(0,t.jsxs)("div",{style:{marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsx)(l.Select,{value:i.budget_duration,onChange:e=>r(n,"budget_duration",e),style:{width:130},options:c.map(e=>({value:e.value,label:e.label}))}),(0,t.jsx)(d.InputNumber,{step:.01,min:0,precision:2,value:i.max_budget??void 0,onChange:e=>r(n,"max_budget",e??null),placeholder:"Max spend ($)",style:{width:160},prefix:"$"}),(0,t.jsx)(s.Button,{type:"text",danger:!0,size:"small",onClick:()=>{a(e.filter((e,t)=>t!==n))},style:{padding:"0 4px"},children:"✕"})]}),o&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#888",marginTop:3,marginLeft:2},children:["↻ ",o]})]},n)}),(0,t.jsx)(s.Button,{size:"small",onClick:t=>{t.preventDefault(),a([...e,{budget_duration:"24h",max_budget:null}])},children:"+ Add Budget Window"})]})}],319312)},390605,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(602869),a=e.i(599724),r=e.i(482725),i=e.i(91739),n=e.i(500727),o=e.i(531516),d=e.i(696609);e.s(["default",0,({accessToken:e,selectedServers:c,toolPermissions:u,onChange:m,disabled:p=!1})=>{let{data:g=[]}=(0,n.useMCPServers)(),[h,x]=(0,s.useState)({}),[y,f]=(0,s.useState)({}),[b,_]=(0,s.useState)({}),[j,v]=(0,s.useState)({}),w=(0,s.useRef)(u);(0,s.useEffect)(()=>{w.current=u},[u]);let N=(0,s.useMemo)(()=>0===c.length?[]:g.filter(e=>c.includes(e.server_id)),[g,c]),k=async(e,t)=>{f(t=>({...t,[e]:!0})),_(t=>({...t,[e]:""}));try{let s=await (0,l.listMCPTools)(t,e);if(s.error)_(t=>({...t,[e]:s.message||"Failed to fetch tools"})),x(t=>({...t,[e]:[]}));else{let t=s.tools||[];x(s=>({...s,[e]:t}));let l=w.current;if(!l[e]&&t.length>0){let s=t.filter(e=>"delete"!==(0,d.classifyToolOp)(e.name,e.description||"")).map(e=>e.name);m({...l,[e]:s})}}}catch(t){console.error(`Error fetching tools for server ${e}:`,t),_(t=>({...t,[e]:"Failed to fetch tools"})),x(t=>({...t,[e]:[]}))}finally{f(t=>({...t,[e]:!1}))}};(0,s.useEffect)(()=>{N.forEach(t=>{h[t.server_id]||y[t.server_id]||k(t.server_id,e)})},[N,e]);let S=(e,t)=>{m({...u,[e]:t})};return 0===c.length?null:(0,t.jsx)("div",{className:"space-y-4",children:N.map(e=>{let s=e.server_name||e.alias||e.server_id,l=h[e.server_id]||[],n=u[e.server_id]||[],d=y[e.server_id],c=b[e.server_id],g=j[e.server_id]??"crud";return(0,t.jsxs)("div",{className:"border rounded-lg bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-4 border-b bg-white rounded-t-lg",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-semibold text-gray-900",children:s}),e.description&&(0,t.jsx)(a.Text,{className:"text-sm text-gray-500",children:e.description})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[!p&&l.length>0&&(0,t.jsx)(i.Radio.Group,{value:g,onChange:t=>v(s=>({...s,[e.server_id]:t.target.value})),size:"small",optionType:"button",buttonStyle:"solid",options:[{label:"Risk Groups",value:"crud"},{label:"Flat List",value:"flat"}]}),!p&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;let s;return s=h[t=e.server_id]||[],void m({...u,[t]:s.map(e=>e.name)})},disabled:d,children:"Select All"}),(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;return t=e.server_id,void m({...u,[t]:[]})},disabled:d,children:"Deselect All"})]})]})]}),(0,t.jsxs)("div",{className:"p-4",children:[d&&(0,t.jsxs)("div",{className:"flex items-center justify-center py-8",children:[(0,t.jsx)(r.Spin,{size:"large"}),(0,t.jsx)(a.Text,{className:"ml-3 text-gray-500",children:"Loading tools..."})]}),c&&!d&&(0,t.jsxs)("div",{className:"p-4 bg-red-50 border border-red-200 rounded-lg text-center",children:[(0,t.jsx)(a.Text,{className:"text-red-600 font-medium",children:"Unable to load tools"}),(0,t.jsx)(a.Text,{className:"text-sm text-red-500 mt-1",children:c})]}),!d&&!c&&l.length>0&&"crud"===g&&(0,t.jsx)(o.default,{tools:l,value:u[e.server_id]?n:void 0,onChange:t=>S(e.server_id,t),readOnly:p}),!d&&!c&&l.length>0&&"flat"===g&&(0,t.jsx)("div",{className:"space-y-2",children:l.map(s=>{let l=n.includes(s.name);return(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("input",{type:"checkbox",checked:l,onChange:()=>{if(p)return;let t=l?n.filter(e=>e!==s.name):[...n,s.name];S(e.server_id,t)},disabled:p,className:"mt-0.5"}),(0,t.jsx)("div",{className:"flex-1 min-w-0",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900",children:s.name}),(0,t.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["- ",s.description||"No description"]})]})})]},s.name)})}),!d&&!c&&0===l.length&&(0,t.jsx)("div",{className:"text-center py-6",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"No tools available"})})]})]},e.server_id)})})}])},364769,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(237016),a=e.i(464571),r=e.i(888259);e.s(["default",0,({apiKey:e})=>{let[i,n]=(0,s.useState)(!1);return(0,t.jsxs)("div",{children:[(0,t.jsxs)("p",{className:"mb-2",children:["Please save this secret key somewhere safe and accessible. For security reasons,"," ",(0,t.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]}),(0,t.jsx)("p",{className:"text-sm text-gray-600 mt-3 mb-1",children:"Virtual Key:"}),(0,t.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,t.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal",margin:0},children:e})}),(0,t.jsx)(l.CopyToClipboard,{text:e,onCopy:()=>{n(!0),r.default.success("Key copied to clipboard"),setTimeout(()=>n(!1),2e3)},children:(0,t.jsx)(a.Button,{type:"primary",style:{marginTop:12},children:i?"Copied!":"Copy Virtual Key"})})]})}])},702597,e=>{"use strict";var t=e.i(843476),s=e.i(207082),l=e.i(109799),a=e.i(510674),r=e.i(109034),i=e.i(292639),n=e.i(135214),o=e.i(500330),d=e.i(827252),c=e.i(912598),u=e.i(677667),m=e.i(130643),p=e.i(898667),g=e.i(994388),h=e.i(309426),x=e.i(350967),y=e.i(599724),f=e.i(779241),b=e.i(629569),_=e.i(464571),j=e.i(808613),v=e.i(311451),w=e.i(212931),N=e.i(91739),k=e.i(199133),S=e.i(790848),C=e.i(262218),T=e.i(592968),I=e.i(898586),A=e.i(374009),L=e.i(271645),F=e.i(708347),M=e.i(552130),O=e.i(557662),E=e.i(9314),P=e.i(860585),B=e.i(82946),$=e.i(392110),R=e.i(533882),D=e.i(844565),V=e.i(651904),z=e.i(939510),U=e.i(460285),G=e.i(663435),K=e.i(363256),q=e.i(575260),W=e.i(371455),H=e.i(128233),Q=e.i(319312),J=e.i(355619),Y=e.i(75921),X=e.i(234713),Z=e.i(390605),ee=e.i(727749),et=e.i(602869),es=e.i(364769),el=e.i(435451),ea=e.i(916940);let{Option:er}=k.Select,ei=async(e,t,s,l)=>{try{if(null===e||null===t)return[];if(null!==s)return(await (0,et.modelAvailableCall)(s,e,t,!0,l,!0)).data.map(e=>e.id);return[]}catch(e){return console.error("Error fetching user models:",e),[]}},en=async(e,t,s,l)=>{try{if(null===e||null===t)return;if(null!==s){let a=(await (0,et.modelAvailableCall)(s,e,t)).data.map(e=>e.id);l(a)}}catch(e){console.error("Error fetching user models:",e)}};e.s(["default",0,({team:e,teams:eo,data:ed,addKey:ec,autoOpenCreate:eu,prefillData:em})=>{let{accessToken:ep,userId:eg,userRole:eh,premiumUser:ex}=(0,n.default)(),ey=ex||null!=eh&&F.rolesWithWriteAccess.includes(eh),{data:ef,isLoading:eb}=(0,l.useOrganizations)(),{data:e_,isLoading:ej}=(0,a.useProjects)(),{data:ev}=(0,i.useUISettings)(),{data:ew}=(0,r.useTags)(),eN=!!ev?.values?.enable_projects_ui,ek=!!ev?.values?.disable_custom_api_keys,eS=ew?Object.values(ew).map(e=>({value:e.name,label:e.name})):[],eC=(0,c.useQueryClient)(),[eT]=j.Form.useForm(),[eI,eA]=(0,L.useState)(!1),[eL,eF]=(0,L.useState)(null),[eM,eO]=(0,L.useState)(null),[eE,eP]=(0,L.useState)([]),[eB,e$]=(0,L.useState)([]),[eR,eD]=(0,L.useState)("you"),[eV,ez]=(0,L.useState)(!1),[eU,eG]=(0,L.useState)(null),[eK,eq]=(0,L.useState)([]),[eW,eH]=(0,L.useState)([]),[eQ,eJ]=(0,L.useState)([]),[eY,eX]=(0,L.useState)([]),[eZ,e0]=(0,L.useState)(e),[e1,e2]=(0,L.useState)(null),[e4,e3]=(0,L.useState)(null),[e5,e6]=(0,L.useState)(!1),[e7,e9]=(0,L.useState)(null),[e8,te]=(0,L.useState)({}),[tt,ts]=(0,L.useState)([]),[tl,ta]=(0,L.useState)(!1),[tr,ti]=(0,L.useState)([]),[tn,to]=(0,L.useState)([]),[td,tc]=(0,L.useState)("llm_api"),[tu,tm]=(0,L.useState)({}),[tp,tg]=(0,L.useState)(!1),[th,tx]=(0,L.useState)("30d"),[ty,tf]=(0,L.useState)(null),[tb,t_]=(0,L.useState)([]),[tj,tv]=(0,L.useState)({}),[tw,tN]=(0,L.useState)(0),[tk,tS]=(0,L.useState)(0),[tC,tT]=(0,L.useState)([]),[tI,tA]=(0,L.useState)(null),tL=()=>{eA(!1),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)},tF=()=>{eA(!1),eF(null),e0(null),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)};(0,L.useEffect)(()=>{eg&&eh&&ep&&en(eg,eh,ep,eP)},[ep,eg,eh]),(0,L.useEffect)(()=>{ep&&(0,et.getAgentsList)(ep).then(e=>tT(e?.agents||[])).catch(()=>tT([]))},[ep]),(0,L.useEffect)(()=>{let e=async()=>{try{let e=(await (0,et.getPoliciesList)(ep)).policies.map(e=>e.policy_name);eH(e)}catch(e){console.error("Failed to fetch policies:",e)}},t=async()=>{try{let e=await (0,et.getPromptsList)(ep);eJ(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}};(async()=>{try{let e=(await (0,et.getGuardrailsList)(ep)).guardrails.map(e=>e.guardrail_name);eq(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})(),e(),t()},[ep]),(0,L.useEffect)(()=>{(async()=>{try{if(ep){let e=sessionStorage.getItem("possibleUserRoles");if(e)te(JSON.parse(e));else{let e=await (0,et.getPossibleUserRoles)(ep);sessionStorage.setItem("possibleUserRoles",JSON.stringify(e)),te(e)}}}catch(e){console.error("Error fetching possible user roles:",e)}})()},[ep]),(0,L.useEffect)(()=>{if(eu&&!eV&&eo&&eh&&F.rolesWithWriteAccess.includes(eh)&&(eA(!0),ez(!0),em)){if(em.owned_by&&("another_user"===em.owned_by&&"Admin"!==eh?eD("you"):eD(em.owned_by)),em.team_id){let e=eo?.find(e=>e.team_id===em.team_id)||null;e&&(e0(e),eT.setFieldsValue({team_id:em.team_id}))}em.key_alias&&eT.setFieldsValue({key_alias:em.key_alias}),em.models&&em.models.length>0&&eG(em.models),em.key_type&&(tc(em.key_type),eT.setFieldsValue({key_type:em.key_type}))}},[eu,em,eo,eV,eT,eh]);let tM=eB.includes("no-default-models")&&!eZ,tO=async e=>{try{let t,l=e?.key_alias??"",a=e?.team_id??null;if((ed?.filter(e=>e.team_id===a).map(e=>e.key_alias)??[]).includes(l))throw Error(`Key alias ${l} already exists for team with ID ${a}, please provide another key alias`);if(ee.default.info("Making API Call"),eA(!0),"you"===eR)e.user_id=eg;else if("agent"===eR){if(!tI)return void ee.default.fromBackend("Please select an agent");e.agent_id=tI}let r={};try{r=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}if("service_account"===eR&&(r.service_account_id=e.key_alias),eY.length>0&&(r={...r,logging:eY.filter(e=>e.callback_name)}),tn.length>0){let e=(0,O.mapDisplayToInternalNames)(tn);r={...r,litellm_disabled_callbacks:e}}if(tp&&(e.auto_rotate=!0,e.rotation_interval=th),e.duration&&""!==e.duration.trim()||(e.duration=null),e.metadata=JSON.stringify(r),e.disable_global_guardrails||delete e.disable_global_guardrails,e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission={vector_stores:e.allowed_vector_store_ids},delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{servers:t,accessGroups:s}=e.allowed_mcp_servers_and_groups;t&&t.length>0&&(e.object_permission.mcp_servers=t),s&&s.length>0&&(e.object_permission.mcp_access_groups=s),delete e.allowed_mcp_servers_and_groups}let i=e.mcp_tool_permissions||{};if(Object.keys(i).length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_tool_permissions=i),delete e.mcp_tool_permissions,e.allowed_mcp_access_groups&&e.allowed_mcp_access_groups.length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_access_groups=e.allowed_mcp_access_groups,delete e.allowed_mcp_access_groups),e.allowed_agents_and_groups&&(e.allowed_agents_and_groups.agents?.length>0||e.allowed_agents_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{agents:t,accessGroups:s}=e.allowed_agents_and_groups;t&&t.length>0&&(e.object_permission.agents=t),s&&s.length>0&&(e.object_permission.agent_access_groups=s),delete e.allowed_agents_and_groups}Object.keys(tu).length>0&&(e.aliases=JSON.stringify(tu)),ty?.router_settings&&Object.values(ty.router_settings).some(e=>null!=e&&""!==e)&&(e.router_settings=ty.router_settings);let n=tb.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);n.length>0&&(e.budget_limits=n),Object.keys(tj).length>0&&(e.budget_fallbacks=tj),t="service_account"===eR?await (0,et.keyCreateServiceAccountCall)(ep,e):await (0,et.keyCreateCall)(ep,eg,e),ec(t),eC.invalidateQueries({queryKey:s.keyKeys.lists()}),eF(t.key),eO(t.soft_budget),ee.default.success("Virtual Key Created"),eT.resetFields(),t_([]),tv({}),tN(e=>e+1),localStorage.removeItem("userData"+eg)}catch(t){let e=(e=>{let t;if(!(t=!e||"object"!=typeof e||e instanceof Error?String(e):JSON.stringify(e)).includes("/key/generate")&&!t.includes("KeyManagementRoutes.KEY_GENERATE"))return`Error creating the key: ${e}`;let s=t;try{if(!e||"object"!=typeof e||e instanceof Error){let e=t.match(/\{[\s\S]*\}/);if(e){let t=JSON.parse(e[0]),l=t?.error||t;l?.message&&(s=l.message)}}else{let t=e?.error||e;t?.message&&(s=t.message)}}catch(e){}return t.includes("team_member_permission_error")||s.includes("Team member does not have permissions")?"Team member does not have permission to generate key for this team. Ask your proxy admin to configure the team member permission settings.":`Error creating the key: ${e}`})(t);ee.default.fromBackend(e)}};(0,L.useEffect)(()=>{if(e4){let e=e_?.find(e=>e.project_id===e4);e$(e?.models??[]),eT.setFieldValue("models",[]);return}eg&&eh&&ep&&ei(eg,eh,ep,eZ?.team_id??null).then(e=>{e$(Array.from(new Set([...eZ?.models??[],...e])))}),eU||eT.setFieldValue("models",[]),eT.setFieldValue("allowed_mcp_servers_and_groups",{servers:[],accessGroups:[]})},[eZ,e4,ep,eg,eh,eT]),(0,L.useEffect)(()=>{if(!eU||0===eU.length||!eB||0===eB.length)return;let e=eU.filter(e=>eB.includes(e));e.length>0&&eT.setFieldsValue({models:e}),eG(null)},[eU,eB,eT]),(0,L.useEffect)(()=>{if(!e4||!eo)return;let e=e_?.find(e=>e.project_id===e4);if(!e?.team_id||eZ?.team_id===e.team_id)return;let t=eo.find(t=>t.team_id===e.team_id)||null;t&&(e0(t),eT.setFieldValue("team_id",t.team_id))},[eo,e4,e_]);let tE=async e=>{if(!e)return void ts([]);ta(!0);try{let t=new URLSearchParams;if(t.append("user_email",e),null==ep)return;let s=(await (0,et.userFilterUICall)(ep,t)).map(e=>({label:`${e.user_email} (${e.user_id})`,value:e.user_id,user:e}));ts(s)}catch(e){console.error("Error fetching users:",e),ee.default.fromBackend("Failed to search for users")}finally{ta(!1)}},tP=(0,L.useCallback)((0,A.default)(e=>tE(e),300),[ep]);return(0,t.jsxs)("div",{children:[eh&&F.rolesWithWriteAccess.includes(eh)&&(0,t.jsx)(g.Button,{className:"mx-auto",onClick:()=>eA(!0),"data-testid":"create-key-button",children:"+ Create New Key"}),(0,t.jsx)(w.Modal,{open:eI,width:1e3,footer:null,onOk:tL,onCancel:tF,children:(0,t.jsxs)(j.Form,{form:eT,onFinish:tO,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Ownership"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Owned By"," ",(0,t.jsx)(T.Tooltip,{title:"Select who will own this Virtual Key",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),className:"mb-4",children:(0,t.jsxs)(N.Radio.Group,{onChange:e=>eD(e.target.value),value:eR,children:[(0,t.jsx)(N.Radio,{value:"you",children:"You"}),(0,t.jsx)(N.Radio,{value:"service_account",children:"Service Account"}),"Admin"===eh&&(0,t.jsx)(N.Radio,{value:"another_user",children:"Another User"}),(0,t.jsxs)(N.Radio,{value:"agent",children:["Agent ",(0,t.jsx)(C.Tag,{color:"purple",children:"New"})]})]})}),"another_user"===eR&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["User ID"," ",(0,t.jsx)(T.Tooltip,{title:"The user who will own this key and be responsible for its usage",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"user_id",className:"mt-4",rules:[{required:"another_user"===eR,message:"Please input the user ID of the user you are assigning the key to"}],children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",marginBottom:"8px"},children:[(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Type email to search for users",filterOption:!1,onSearch:e=>{tP(e)},onSelect:(e,t)=>{let s;return s=t.user,void eT.setFieldsValue({user_id:s.user_id})},options:tt,loading:tl,allowClear:!0,style:{width:"100%"},notFoundContent:tl?"Searching...":"No users found"}),(0,t.jsx)(_.Button,{onClick:()=>e6(!0),style:{marginLeft:"8px"},children:"Create User"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Search by email to find users"})]})}),"agent"===eR&&(0,t.jsxs)("div",{className:"mt-4 p-4 bg-purple-50 border border-purple-200 rounded-md",children:[(0,t.jsx)("div",{className:"mb-3",children:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:["Select Agent ",(0,t.jsx)("span",{className:"text-red-500",children:"*"})]})}),(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Select an agent",style:{width:"100%"},value:tI,onChange:e=>tA(e),filterOption:(e,t)=>t?.label?.toLowerCase().includes(e.toLowerCase()),options:tC.map(e=>({label:e.agent_name||e.agent_id,value:e.agent_id}))}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"This key will be used by the selected agent to make requests to LiteLLM"})]}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(T.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",className:"mt-4",children:(0,t.jsx)(K.default,{organizations:ef,loading:eb,disabled:"Admin"!==eh,onChange:e=>{e2(e||null),e0(null),e3(null),eT.setFieldValue("team_id",void 0),eT.setFieldValue("project_id",void 0)}})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Team"," ",(0,t.jsx)(T.Tooltip,{title:"The team this key belongs to, which determines available models and budget limits",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"team_id",initialValue:e?e.team_id:null,className:"mt-4",rules:[{required:"service_account"===eR,message:"Please select a team for the service account"}],help:"service_account"===eR?"required":"",children:(0,t.jsx)(G.default,{disabled:null!==e4,organizationId:e1,onTeamSelect:e=>{e0(e),e3(null),eT.setFieldValue("project_id",void 0),e?.organization_id?(e2(e.organization_id),eT.setFieldValue("organization_id",e.organization_id)):e||(e2(null),eT.setFieldValue("organization_id",void 0))}})}),eN&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Project"," ",(0,t.jsx)(T.Tooltip,{title:"Assign this key to a project. Selecting a project will lock the team to the project's team.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"project_id",className:"mt-4",children:(0,t.jsx)(q.default,{projects:e_,teamId:eZ?.team_id,loading:ej||!eo,onChange:e=>{if(!e){e3(null),e0(null),eT.setFieldValue("team_id",void 0);return}e3(e)}})})]}),tM&&(0,t.jsx)("div",{className:"mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md",children:(0,t.jsx)(y.Text,{className:"text-blue-800 text-sm",children:"Please select a team to continue configuring your Virtual Key. If you do not see any teams, please contact your Proxy Admin to either provide you with access to models or to add you to a team."})}),!tM&&(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Details"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["you"===eR||"another_user"===eR?"Key Name":"Service Account ID"," ",(0,t.jsx)(T.Tooltip,{title:"you"===eR||"another_user"===eR?"A descriptive name to identify this key":"Unique identifier for this service account",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_alias",rules:[{required:!0,message:`Please input a ${"you"===eR?"key name":"service account ID"}`}],help:"required",children:(0,t.jsx)(f.TextInput,{placeholder:""})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Models"," ",(0,t.jsx)(T.Tooltip,{title:"Select which models this key can access. Choose 'All Team Models' to grant access to all models available to the team. Leave empty to allow access to all models.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",rules:[],help:"management"===td||"read_only"===td?"Models field is disabled for this key type":"optional - leave empty to allow access to all models",className:"mt-4",children:(0,t.jsxs)(k.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:"management"===td||"read_only"===td,onChange:e=>{e.includes("all-team-models")&&eT.setFieldsValue({models:["all-team-models"]})},children:[!e4&&(0,t.jsx)(er,{value:"all-team-models",children:"All Team Models"},"all-team-models"),eB.map(e=>(0,t.jsx)(er,{value:e,children:(0,J.getModelDisplayName)(e)},e))]})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Key Type"," ",(0,t.jsx)(T.Tooltip,{title:"Select the type of key to determine what routes and operations this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_type",initialValue:"llm_api",className:"mt-4",children:(0,t.jsxs)(k.Select,{defaultValue:"llm_api",placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",onChange:e=>{tc(e),("management"===e||"read_only"===e)&&eT.setFieldsValue({models:[]})},children:[(0,t.jsx)(er,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"AI APIs"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(er,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Management"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only management routes (user/team/key management)"})]})}),(0,t.jsx)(er,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Full Access"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})})]})})]}),!tM&&(0,t.jsx)("div",{className:"mb-8",children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)(b.Title,{className:"m-0",children:"Optional Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum amount in USD this key can spend. When reached, the key will be blocked from making further requests",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",help:`Budget cannot exceed team max budget: $${e?.max_budget!==null&&e?.max_budget!==void 0?e?.max_budget:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.max_budget&&s>e.max_budget)throw Error(`Budget cannot exceed team max budget: $${(0,o.formatNumberWithCommas)(e.max_budget,4)}`)}}],children:(0,t.jsx)(el.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(T.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",help:`Team Reset Budget: ${e?.budget_duration!==null&&e?.budget_duration!==void 0?e?.budget_duration:"None"}`,children:(0,t.jsx)(P.default,{onChange:e=>eT.setFieldValue("budget_duration",e)})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(T.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(Q.BudgetWindowsEditor,{value:tb,onChange:t_})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(T.Tooltip,{title:"When a model exceeds its per-model budget (model_max_budget), requests automatically reroute to fallback models instead of failing. Configure per-model budgets in Advanced Settings.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(H.BudgetFallbacksEditor,{value:tj,onChange:tv,availableModels:eB},tw)}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Tokens per minute Limit (TPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of tokens this key can process per minute. Helps control usage and costs",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tpm_limit",help:`TPM cannot exceed team TPM limit: ${e?.tpm_limit!==null&&e?.tpm_limit!==void 0?e?.tpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.tpm_limit&&s>e.tpm_limit)throw Error(`TPM limit cannot exceed team TPM limit: ${e.tpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"tpm",name:"tpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Requests per minute Limit (RPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of API requests this key can make per minute. Helps prevent abuse and manage load",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"rpm_limit",help:`RPM cannot exceed team RPM limit: ${e?.rpm_limit!==null&&e?.rpm_limit!==void 0?e?.rpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.rpm_limit&&s>e.rpm_limit)throw Error(`RPM limit cannot exceed team RPM limit: ${e.rpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"rpm",name:"rpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:ey?"Select existing guardrails or enter new ones":"Premium feature - Upgrade to set guardrails by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter guardrails":"Premium feature - Upgrade to set guardrails by key",options:eK.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"disable_global_guardrails",className:"mt-4",valuePropName:"checked",help:ey?"Bypass global guardrails for this key":"Premium feature - Upgrade to disable global guardrails by key",children:(0,t.jsx)(S.Switch,{disabled:!ey,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(T.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",className:"mt-4",help:ex?"Select existing policies or enter new ones":"Premium feature - Upgrade to set policies by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter policies":"Premium feature - Upgrade to set policies by key",options:eW.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Prompts"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific prompt templates",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/prompt_management",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"prompts",className:"mt-4",help:ex?"Select existing prompts or enter new ones":"Premium feature - Upgrade to set prompts by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:eQ.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(T.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",className:"mt-4",help:"Select access groups to assign to this key",children:(0,t.jsx)(E.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Pass Through Routes"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific pass through routes",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"allowed_passthrough_routes",className:"mt-4",help:ex?"Select existing pass through routes or enter new ones":"Premium feature - Upgrade to set pass through routes by key",children:(0,t.jsx)(D.default,{onChange:e=>eT.setFieldValue("allowed_passthrough_routes",e),value:eT.getFieldValue("allowed_passthrough_routes"),accessToken:ep,placeholder:ex?"Select or enter pass through routes":"Premium feature - Upgrade to set pass through routes by key",disabled:!ex,teamId:eZ?eZ.team_id:null})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,t.jsx)(T.Tooltip,{title:"Select which vector stores this key can access. If none selected, the key will have access to all available vector stores",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this key can access. Leave empty for access to all vector stores",children:(0,t.jsx)(ea.default,{onChange:e=>eT.setFieldValue("allowed_vector_store_ids",e),value:eT.getFieldValue("allowed_vector_store_ids"),accessToken:ep,placeholder:"Select vector stores (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Metadata"," ",(0,t.jsx)(T.Tooltip,{title:"JSON object with additional information about this key. Used for tracking or custom logic",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"metadata",className:"mt-4",children:(0,t.jsx)(v.Input.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Tags"," ",(0,t.jsx)(T.Tooltip,{title:"Tags for tracking spend and/or doing tag-based routing. Used for analytics and filtering",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tags",className:"mt-4",help:"Tags for tracking spend and/or doing tag-based routing.",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",tokenSeparators:[","],options:eS})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"MCP Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(T.Tooltip,{title:"Select which MCP servers or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",help:"Select MCP servers or access groups this key can access",children:(0,t.jsx)(Y.default,{onChange:e=>eT.setFieldValue("allowed_mcp_servers_and_groups",e),value:eT.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:ep,teamId:eZ?.team_id??null,placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(j.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(j.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(Z.default,{accessToken:ep,selectedServers:(eT.getFieldValue("allowed_mcp_servers_and_groups")?.servers||[]).filter(e=>e!==X.NO_MCP_SERVERS_SENTINEL),toolPermissions:eT.getFieldValue("mcp_tool_permissions")||{},onChange:e=>eT.setFieldsValue({mcp_tool_permissions:e})})})})]})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Agent Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Agents"," ",(0,t.jsx)(T.Tooltip,{title:"Select which agents or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_agents_and_groups",help:"Select agents or access groups this key can access",children:(0,t.jsx)(M.default,{onChange:e=>eT.setFieldValue("allowed_agents_and_groups",e),value:eT.getFieldValue("allowed_agents_and_groups"),accessToken:ep,placeholder:"Select agents or access groups (optional)"})})})]}),ex?(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!0,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]}):(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Key-level logging settings is an enterprise feature, get in touch -",(0,t.jsx)("a",{href:"https://www.litellm.ai/enterprise",target:"_blank",children:"https://www.litellm.ai/enterprise"})]}),placement:"top",children:(0,t.jsxs)("div",{style:{position:"relative"},children:[(0,t.jsx)("div",{style:{opacity:.5},children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!1,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]})}),(0,t.jsx)("div",{style:{position:"absolute",inset:0,cursor:"not-allowed"}})]})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Router Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4 w-full",children:(0,t.jsx)(U.default,{accessToken:ep||"",value:ty||void 0,onChange:tf,modelData:eE.length>0?{data:eE.map(e=>({model_name:e}))}:void 0},tk)})})]},`router-settings-accordion-${tk}`),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Model Aliases"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)(y.Text,{className:"text-sm text-gray-600 mb-4",children:"Create custom aliases for models that can be used in API calls. This allows you to create shortcuts for specific models."}),(0,t.jsx)(R.default,{accessToken:ep,initialModelAliases:tu,onAliasUpdate:tm,showExampleConfig:!1})]})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Key Lifecycle"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)($.default,{form:eT,autoRotationEnabled:tp,onAutoRotationChange:tg,rotationInterval:th,onRotationIntervalChange:tx,isCreateMode:!0})})}),(0,t.jsx)(j.Form.Item,{name:"duration",hidden:!0,initialValue:null,children:(0,t.jsx)(v.Input,{})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("b",{children:"Advanced Settings"}),(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Learn more about advanced settings in our"," ",(0,t.jsx)("a",{href:et.proxyBaseUrl?`${et.proxyBaseUrl}/#/key%20management/generate_key_fn_key_generate_post`:"/#/key%20management/generate_key_fn_key_generate_post",target:"_blank",rel:"noopener noreferrer",className:"text-blue-400 hover:text-blue-300",children:"documentation"})]}),children:(0,t.jsx)(d.InfoCircleOutlined,{className:"text-gray-400 hover:text-gray-300 cursor-help"})})]})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(B.default,{schemaComponent:"GenerateKeyRequest",form:eT,excludedFields:["key_alias","team_id","organization_id","models","duration","metadata","tags","guardrails","max_budget","budget_duration","tpm_limit","rpm_limit",...ek?["key"]:[]]})})]})]})]})}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(_.Button,{htmlType:"submit",disabled:tM,style:{opacity:tM?.5:1},children:"Create Key"})})]})}),e5&&(0,t.jsx)(w.Modal,{title:"Create New User",open:e5,onCancel:()=>e6(!1),footer:null,width:800,children:(0,t.jsx)(W.CreateUserButton,{userID:eg,accessToken:ep,teams:eo,possibleUIRoles:e8,onUserCreated:e=>{e9(e),eT.setFieldsValue({user_id:e}),e6(!1)},isEmbedded:!0})}),eL&&(0,t.jsx)(w.Modal,{open:eI,onOk:tL,onCancel:tF,footer:null,children:(0,t.jsxs)(x.Grid,{numItems:1,className:"gap-2 w-full",children:[(0,t.jsx)(b.Title,{children:"Save your Key"}),(0,t.jsx)(h.Col,{numColSpan:1,children:null!=eL?(0,t.jsx)(es.default,{apiKey:eL}):(0,t.jsx)(y.Text,{children:"Key being created, this might take 30s"})})]})})]})},"fetchTeamModels",0,ei,"fetchUserModels",0,en],702597)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0y5ybr0-5yc_z.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.xp85h9ki~9t.js similarity index 66% rename from litellm/proxy/_experimental/out/_next/static/chunks/0y5ybr0-5yc_z.js rename to litellm/proxy/_experimental/out/_next/static/chunks/0.xp85h9ki~9t.js index a2b2359b381..24c8f4e7454 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0y5ybr0-5yc_z.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0.xp85h9ki~9t.js @@ -1,4 +1,4 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,486794,(e,t,n)=>{t.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],l=0;l{"use strict";var l=e.r(486794),r={"text/plain":"Text","text/html":"Url",default:"Text"};t.exports=function(e,t){var n,o,a,i,c,s,u,d,p=!1;t||(t={}),a=t.debug||!1;try{if(c=l(),s=document.createRange(),u=document.getSelection(),(d=document.createElement("span")).textContent=e,d.ariaHidden="true",d.style.all="unset",d.style.position="fixed",d.style.top=0,d.style.clip="rect(0, 0, 0, 0)",d.style.whiteSpace="pre",d.style.webkitUserSelect="text",d.style.MozUserSelect="text",d.style.msUserSelect="text",d.style.userSelect="text",d.addEventListener("copy",function(n){if(n.stopPropagation(),t.format)if(n.preventDefault(),void 0===n.clipboardData){a&&console.warn("unable to use e.clipboardData"),a&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var l=r[t.format]||r.default;window.clipboardData.setData(l,e)}else n.clipboardData.clearData(),n.clipboardData.setData(t.format,e);t.onCopy&&(n.preventDefault(),t.onCopy(n.clipboardData))}),document.body.appendChild(d),s.selectNodeContents(d),u.addRange(s),!document.execCommand("copy"))throw Error("copy command was unsuccessful");p=!0}catch(l){a&&console.error("unable to copy using execCommand: ",l),a&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(t.format||"text",e),t.onCopy&&t.onCopy(window.clipboardData),p=!0}catch(l){a&&console.error("unable to copy using clipboardData: ",l),a&&console.error("falling back to prompt"),n="message"in t?t.message:"Copy to clipboard: #{key}, Enter",o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C",i=n.replace(/#{\s*key\s*}/g,o),window.prompt(i,e)}}finally{u&&("function"==typeof u.removeRange?u.removeRange(s):u.removeAllRanges()),d&&document.body.removeChild(d),c()}return p}},464571,e=>{"use strict";var t=e.i(920228);e.s(["Button",()=>t.default])},735049,e=>{"use strict";var t=e.i(654310),n=function(e){if((0,t.default)()&&window.document.documentElement){var n=Array.isArray(e)?e:[e],l=window.document.documentElement;return n.some(function(e){return e in l.style})}return!1},l=function(e,t){if(!n(e))return!1;var l=document.createElement("div"),r=l.style[e];return l.style[e]=t,l.style[e]!==r};e.s(["isStyleSupport",0,function(e,t){return Array.isArray(e)||void 0===t?n(e):l(e,t)}])},898586,401361,335771,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(8211),l=e.i(931067);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"}}]},name:"edit",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:r}))});e.s(["default",0,a],401361);var i=e.i(343794),c=e.i(430073),s=e.i(876556),u=e.i(174428),d=e.i(914949),p=e.i(529681),f=e.i(611935),m=e.i(735049),g=e.i(242064),b=e.i(929447),y=e.i(491816);let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M864 170h-60c-4.4 0-8 3.6-8 8v518H310v-73c0-6.7-7.8-10.5-13-6.3l-141.9 112a8 8 0 000 12.6l141.9 112c5.3 4.2 13 .4 13-6.3v-75h498c35.3 0 64-28.7 64-64V178c0-4.4-3.6-8-8-8z"}}]},name:"enter",theme:"outlined"};var h=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:v}))}),x=e.i(404948),O=e.i(763731),E=e.i(635432),w=e.i(183293),S=e.i(246422);e.i(765846);var j=e.i(896091);let C=(0,S.genStyleHooks)("Typography",e=>{let t,{componentCls:n,titleMarginTop:l}=e;return{[n]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({color:e.colorText,wordBreak:"break-word",lineHeight:e.lineHeight,[`&${n}-secondary`]:{color:e.colorTextDescription},[`&${n}-success`]:{color:e.colorSuccessText},[`&${n}-warning`]:{color:e.colorWarningText},[`&${n}-danger`]:{color:e.colorErrorText,"a&:active, a&:focus":{color:e.colorErrorTextActive},"a&:hover":{color:e.colorErrorTextHover}},[`&${n}-disabled`]:{color:e.colorTextDisabled,cursor:"not-allowed",userSelect:"none"},[` +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,735049,e=>{"use strict";var t=e.i(654310),n=function(e){if((0,t.default)()&&window.document.documentElement){var n=Array.isArray(e)?e:[e],l=window.document.documentElement;return n.some(function(e){return e in l.style})}return!1},l=function(e,t){if(!n(e))return!1;var l=document.createElement("div"),r=l.style[e];return l.style[e]=t,l.style[e]!==r};e.s(["isStyleSupport",0,function(e,t){return Array.isArray(e)||void 0===t?n(e):l(e,t)}])},190144,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"}}]},name:"copy",theme:"outlined"};var r=e.i(9583),o=n.forwardRef(function(e,o){return n.createElement(r.default,(0,t.default)({},e,{ref:o,icon:l}))});e.s(["default",0,o],190144)},486794,(e,t,n)=>{t.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],l=0;l{"use strict";var l=e.r(486794),r={"text/plain":"Text","text/html":"Url",default:"Text"};t.exports=function(e,t){var n,o,a,i,c,s,u,d,p=!1;t||(t={}),a=t.debug||!1;try{if(c=l(),s=document.createRange(),u=document.getSelection(),(d=document.createElement("span")).textContent=e,d.ariaHidden="true",d.style.all="unset",d.style.position="fixed",d.style.top=0,d.style.clip="rect(0, 0, 0, 0)",d.style.whiteSpace="pre",d.style.webkitUserSelect="text",d.style.MozUserSelect="text",d.style.msUserSelect="text",d.style.userSelect="text",d.addEventListener("copy",function(n){if(n.stopPropagation(),t.format)if(n.preventDefault(),void 0===n.clipboardData){a&&console.warn("unable to use e.clipboardData"),a&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var l=r[t.format]||r.default;window.clipboardData.setData(l,e)}else n.clipboardData.clearData(),n.clipboardData.setData(t.format,e);t.onCopy&&(n.preventDefault(),t.onCopy(n.clipboardData))}),document.body.appendChild(d),s.selectNodeContents(d),u.addRange(s),!document.execCommand("copy"))throw Error("copy command was unsuccessful");p=!0}catch(l){a&&console.error("unable to copy using execCommand: ",l),a&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(t.format||"text",e),t.onCopy&&t.onCopy(window.clipboardData),p=!0}catch(l){a&&console.error("unable to copy using clipboardData: ",l),a&&console.error("falling back to prompt"),n="message"in t?t.message:"Copy to clipboard: #{key}, Enter",o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C",i=n.replace(/#{\s*key\s*}/g,o),window.prompt(i,e)}}finally{u&&("function"==typeof u.removeRange?u.removeRange(s):u.removeAllRanges()),d&&document.body.removeChild(d),c()}return p}},898586,401361,335771,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(8211),l=e.i(931067);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"}}]},name:"edit",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:r}))});e.s(["default",0,a],401361);var i=e.i(343794),c=e.i(430073),s=e.i(876556),u=e.i(174428),d=e.i(914949),p=e.i(529681),f=e.i(611935),m=e.i(735049),g=e.i(242064),b=e.i(929447),y=e.i(491816);let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M864 170h-60c-4.4 0-8 3.6-8 8v518H310v-73c0-6.7-7.8-10.5-13-6.3l-141.9 112a8 8 0 000 12.6l141.9 112c5.3 4.2 13 .4 13-6.3v-75h498c35.3 0 64-28.7 64-64V178c0-4.4-3.6-8-8-8z"}}]},name:"enter",theme:"outlined"};var h=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:v}))}),x=e.i(404948),O=e.i(763731),E=e.i(635432),w=e.i(183293),S=e.i(246422);e.i(765846);var j=e.i(896091);let C=(0,S.genStyleHooks)("Typography",e=>{let t,{componentCls:n,titleMarginTop:l}=e;return{[n]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({color:e.colorText,wordBreak:"break-word",lineHeight:e.lineHeight,[`&${n}-secondary`]:{color:e.colorTextDescription},[`&${n}-success`]:{color:e.colorSuccessText},[`&${n}-warning`]:{color:e.colorWarningText},[`&${n}-danger`]:{color:e.colorErrorText,"a&:active, a&:focus":{color:e.colorErrorTextActive},"a&:hover":{color:e.colorErrorTextHover}},[`&${n}-disabled`]:{color:e.colorTextDisabled,cursor:"not-allowed",userSelect:"none"},[` div&, p `]:{marginBottom:"1em"}},(t={},[1,2,3,4,5].forEach(n=>{t[` @@ -38,4 +38,4 @@ &:focus`]:{color:e.colorSuccess}},[`${e.componentCls}-copy-icon-only`]:{marginInlineStart:0}}),{[` a&-ellipsis, span&-ellipsis - `]:{display:"inline-block",maxWidth:"100%"},"&-ellipsis-single-line":{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis","a&, span&":{verticalAlign:"bottom"},"> code":{paddingBlock:0,maxWidth:"calc(100% - 1.2em)",display:"inline-block",overflow:"hidden",textOverflow:"ellipsis",verticalAlign:"bottom",boxSizing:"content-box"}},"&-ellipsis-multiple-line":{display:"-webkit-box",overflow:"hidden",WebkitLineClamp:3,WebkitBoxOrient:"vertical"}}),{"&-rtl":{direction:"rtl"}})}},()=>({titleMarginTop:"1.2em",titleMarginBottom:"0.5em"})),k=e=>{let{prefixCls:n,"aria-label":l,className:r,style:o,direction:a,maxLength:c,autoSize:s=!0,value:u,onSave:d,onCancel:p,onEnd:f,component:m,enterIcon:g=t.createElement(h,null)}=e,b=t.useRef(null),y=t.useRef(!1),v=t.useRef(null),[w,S]=t.useState(u);t.useEffect(()=>{S(u)},[u]),t.useEffect(()=>{var e;if(null==(e=b.current)?void 0:e.resizableTextArea){let{textArea:e}=b.current.resizableTextArea;e.focus();let{length:t}=e.value;e.setSelectionRange(t,t)}},[]);let j=()=>{d(w.trim())},[k,R,$]=C(n),T=(0,i.default)(n,`${n}-edit-content`,{[`${n}-rtl`]:"rtl"===a,[`${n}-${m}`]:!!m},r,R,$);return k(t.createElement("div",{className:T,style:o},t.createElement(E.default,{ref:b,maxLength:c,value:w,onChange:({target:e})=>{S(e.value.replace(/[\n\r]/g,""))},onKeyDown:({keyCode:e})=>{y.current||(v.current=e)},onKeyUp:({keyCode:e,ctrlKey:t,altKey:n,metaKey:l,shiftKey:r})=>{v.current!==e||y.current||t||n||l||r||(e===x.default.ENTER?(j(),null==f||f()):e===x.default.ESC&&p())},onCompositionStart:()=>{y.current=!0},onCompositionEnd:()=>{y.current=!1},onBlur:()=>{j()},"aria-label":l,rows:1,autoSize:s}),null!==g?(0,O.cloneElement)(g,{className:`${n}-edit-content-confirm`}):null))};var R=e.i(844343),$=e.i(175066);function T(e,n){return t.useMemo(()=>{let t=!!e;return[t,Object.assign(Object.assign({},n),t&&"object"==typeof e?e:null)]},[e])}var I=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let D=t.forwardRef((e,n)=>{let{prefixCls:l,component:r="article",className:o,rootClassName:a,setContentRef:c,children:s,direction:u,style:d}=e,p=I(e,["prefixCls","component","className","rootClassName","setContentRef","children","direction","style"]),{getPrefixCls:m,direction:b,className:y,style:v}=(0,g.useComponentConfig)("typography"),h=c?(0,f.composeRef)(n,c):n,x=m("typography",l),[O,E,w]=C(x),S=(0,i.default)(x,y,{[`${x}-rtl`]:"rtl"===(null!=u?u:b)},o,a,E,w),j=Object.assign(Object.assign({},v),d);return O(t.createElement(r,Object.assign({className:S,style:j,ref:h},p),s))});var P=e.i(121229),B=e.i(190144),A=e.i(739295);function M(e){return!1===e?[!1,!1]:Array.isArray(e)?e:[e]}function H(e,t,n){return!0===e||void 0===e?t:e||n&&t}let z=e=>["string","number"].includes(typeof e),W=({prefixCls:e,copied:n,locale:l,iconOnly:r,tooltips:o,icon:a,tabIndex:c,onCopy:s,loading:u})=>{let d=M(o),p=M(a),{copied:f,copy:m}=null!=l?l:{},g=n?f:m,b=H(d[+!!n],g),v="string"==typeof b?b:g;return t.createElement(y.default,{title:b},t.createElement("button",{type:"button",className:(0,i.default)(`${e}-copy`,{[`${e}-copy-success`]:n,[`${e}-copy-icon-only`]:r}),onClick:s,"aria-label":v,tabIndex:c},n?H(p[1],t.createElement(P.default,null),!0):H(p[0],u?t.createElement(A.default,null):t.createElement(B.default,null),!0)))},L=t.forwardRef(({style:e,children:n},l)=>{let r=t.useRef(null);return t.useImperativeHandle(l,()=>({isExceed:()=>{let e=r.current;return e.scrollHeight>e.clientHeight},getHeight:()=>r.current.clientHeight})),t.createElement("span",{"aria-hidden":!0,ref:r,style:Object.assign({position:"fixed",display:"block",left:0,top:0,pointerEvents:"none",backgroundColor:"rgba(255, 0, 0, 0.65)"},e)},n)});function N(e,t){let n=0,l=[];for(let r=0;rt){let e=t-n;return l.push(String(o).slice(0,e)),l}l.push(o),n=a}return e}let U={display:"-webkit-box",overflow:"hidden",WebkitBoxOrient:"vertical"};function F(e){let{enableMeasure:l,width:r,text:o,children:a,rows:i,expanded:c,miscDeps:d,onEllipsis:p}=e,f=t.useMemo(()=>(0,s.default)(o),[o]),m=t.useMemo(()=>f.reduce((e,t)=>e+(z(t)?String(t).length:1),0),[o]),g=t.useMemo(()=>a(f,!1),[o]),[b,y]=t.useState(null),v=t.useRef(null),h=t.useRef(null),x=t.useRef(null),O=t.useRef(null),E=t.useRef(null),[w,S]=t.useState(!1),[j,C]=t.useState(0),[k,R]=t.useState(0),[$,T]=t.useState(null);(0,u.default)(()=>{l&&r&&m?C(1):C(0)},[r,o,i,l,f]),(0,u.default)(()=>{var e,t,n,l;if(1===j)C(2),T(h.current&&getComputedStyle(h.current).whiteSpace);else if(2===j){let r=!!(null==(e=x.current)?void 0:e.isExceed());C(r?3:4),y(r?[0,m]:null),S(r),R(Math.max((null==(t=x.current)?void 0:t.getHeight())||0,(1===i?0:(null==(n=O.current)?void 0:n.getHeight())||0)+((null==(l=E.current)?void 0:l.getHeight())||0))+1),p(r)}},[j]);let I=b?Math.ceil((b[0]+b[1])/2):0;(0,u.default)(()=>{var e;let[t,n]=b||[0,0];if(t!==n){let l=((null==(e=v.current)?void 0:e.getHeight())||0)>k,r=I;n-t==1&&(r=l?t:n),y(l?[t,r]:[r,n])}},[b,I]);let D=t.useMemo(()=>{if(!l)return a(f,!1);if(3!==j||!b||b[0]!==b[1]){let e=a(f,!1);return[4,0].includes(j)?e:t.createElement("span",{style:Object.assign(Object.assign({},U),{WebkitLineClamp:i})},e)}return a(c?f:N(f,b[0]),w)},[c,j,b,f].concat((0,n.default)(d))),P={width:r,margin:0,padding:0,whiteSpace:"nowrap"===$?"normal":"inherit"};return t.createElement(t.Fragment,null,D,2===j&&t.createElement(t.Fragment,null,t.createElement(L,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i}),ref:x},g),t.createElement(L,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i-1}),ref:O},g),t.createElement(L,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:1}),ref:E},a([],!0))),3===j&&b&&b[0]!==b[1]&&t.createElement(L,{style:Object.assign(Object.assign({},P),{top:400}),ref:v},a(N(f,I),!0)),1===j&&t.createElement("span",{style:{whiteSpace:"inherit"},ref:h}))}let q=({enableEllipsis:e,isEllipsis:n,children:l,tooltipProps:r})=>(null==r?void 0:r.title)&&e?t.createElement(y.default,Object.assign({open:!!n&&void 0},r),l):l;var X=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let K=["delete","mark","code","underline","strong","keyboard","italic"],V=t.forwardRef((e,l)=>{var r;let o,v,h,{prefixCls:x,className:O,style:E,type:w,disabled:S,children:j,ellipsis:C,editable:I,copyable:P,component:B,title:A}=e,M=X(e,["prefixCls","className","style","type","disabled","children","ellipsis","editable","copyable","component","title"]),{getPrefixCls:H,direction:L}=t.useContext(g.ConfigContext),[N]=(0,b.default)("Text"),U=t.useRef(null),V=t.useRef(null),_=H("typography",x),G=(0,p.default)(M,K),[J,Q]=T(I),[Y,Z]=(0,d.default)(!1,{value:Q.editing}),{triggerType:ee=["icon"]}=Q,et=e=>{var t;e&&(null==(t=Q.onStart)||t.call(Q)),Z(e)},en=(o=(0,t.useRef)(void 0),(0,t.useEffect)(()=>{o.current=Y}),o.current);(0,u.default)(()=>{var e;!Y&&en&&(null==(e=V.current)||e.focus())},[Y]);let el=e=>{null==e||e.preventDefault(),et(!0)},[er,eo]=T(P),{copied:ea,copyLoading:ei,onClick:ec}=(({copyConfig:e,children:n})=>{let[l,r]=t.useState(!1),[o,a]=t.useState(!1),i=t.useRef(null),c=()=>{i.current&&clearTimeout(i.current)},s={};e.format&&(s.format=e.format),t.useEffect(()=>c,[]);let u=(0,$.default)(t=>{var l,o,u,d;return l=void 0,o=void 0,u=void 0,d=function*(){var l;null==t||t.preventDefault(),null==t||t.stopPropagation(),a(!0);try{let o="function"==typeof e.text?yield e.text():e.text;(0,R.default)(o||((e,t=!1)=>t&&null==e?[]:Array.isArray(e)?e:[e])(n,!0).join("")||"",s),a(!1),r(!0),c(),i.current=setTimeout(()=>{r(!1)},3e3),null==(l=e.onCopy)||l.call(e,t)}catch(e){throw a(!1),e}},new(u||(u=Promise))(function(e,t){function n(e){try{a(d.next(e))}catch(e){t(e)}}function r(e){try{a(d.throw(e))}catch(e){t(e)}}function a(t){var l;t.done?e(t.value):((l=t.value)instanceof u?l:new u(function(e){e(l)})).then(n,r)}a((d=d.apply(l,o||[])).next())})});return{copied:l,copyLoading:o,onClick:u}})({copyConfig:eo,children:j}),[es,eu]=t.useState(!1),[ed,ep]=t.useState(!1),[ef,em]=t.useState(!1),[eg,eb]=t.useState(!1),[ey,ev]=t.useState(!0),[eh,ex]=T(C,{expandable:!1,symbol:e=>e?null==N?void 0:N.collapse:null==N?void 0:N.expand}),[eO,eE]=(0,d.default)(ex.defaultExpanded||!1,{value:ex.expanded}),ew=eh&&(!eO||"collapsible"===ex.expandable),{rows:eS=1}=ex,ej=t.useMemo(()=>ew&&(void 0!==ex.suffix||ex.onEllipsis||ex.expandable||J||er),[ew,ex,J,er]);(0,u.default)(()=>{eh&&!ej&&(eu((0,m.isStyleSupport)("webkitLineClamp")),ep((0,m.isStyleSupport)("textOverflow")))},[ej,eh]);let[eC,ek]=t.useState(ew),eR=t.useMemo(()=>!ej&&(1===eS?ed:es),[ej,ed,es]);(0,u.default)(()=>{ek(eR&&ew)},[eR,ew]);let e$=ew&&(eC?eg:ef),eT=ew&&1===eS&&eC,eI=ew&&eS>1&&eC,[eD,eP]=t.useState(0),eB=e=>{var t;em(e),ef!==e&&(null==(t=ex.onEllipsis)||t.call(ex,e))};t.useEffect(()=>{let e=U.current;if(eh&&eC&&e){let t,n,l,r=(t=document.createElement("em"),e.appendChild(t),n=e.getBoundingClientRect(),l=t.getBoundingClientRect(),e.removeChild(t),n.left>l.left||l.right>n.right||n.top>l.top||l.bottom>n.bottom);eg!==r&&eb(r)}},[eh,eC,j,eI,ey,eD]),t.useEffect(()=>{let e=U.current;if("u"{ev(!!e.offsetParent)});return t.observe(e),()=>{t.disconnect()}},[eC,ew]);let eA=(v=ex.tooltip,h=Q.text,(0,t.useMemo)(()=>!0===v?{title:null!=h?h:j}:(0,t.isValidElement)(v)?{title:v}:"object"==typeof v?Object.assign({title:null!=h?h:j},v):{title:v},[v,h,j])),eM=t.useMemo(()=>{if(eh&&!eC)return[Q.text,j,A,eA.title].find(z)},[eh,eC,A,eA.title,e$]);return Y?t.createElement(k,{value:null!=(r=Q.text)?r:"string"==typeof j?j:"",onSave:e=>{var t;null==(t=Q.onChange)||t.call(Q,e),et(!1)},onCancel:()=>{var e;null==(e=Q.onCancel)||e.call(Q),et(!1)},onEnd:Q.onEnd,prefixCls:_,className:O,style:E,direction:L,component:B,maxLength:Q.maxLength,autoSize:Q.autoSize,enterIcon:Q.enterIcon}):t.createElement(c.default,{onResize:({offsetWidth:e})=>{eP(e)},disabled:!ew},r=>t.createElement(q,{tooltipProps:eA,enableEllipsis:ew,isEllipsis:e$},t.createElement(D,Object.assign({className:(0,i.default)({[`${_}-${w}`]:w,[`${_}-disabled`]:S,[`${_}-ellipsis`]:eh,[`${_}-ellipsis-single-line`]:eT,[`${_}-ellipsis-multiple-line`]:eI},O),prefixCls:x,style:Object.assign(Object.assign({},E),{WebkitLineClamp:eI?eS:void 0}),component:B,ref:(0,f.composeRef)(r,U,l),direction:L,onClick:ee.includes("text")?el:void 0,"aria-label":null==eM?void 0:eM.toString(),title:A},G),t.createElement(F,{enableMeasure:ew&&!eC,text:j,rows:eS,width:eD,onEllipsis:eB,expanded:eO,miscDeps:[ea,eO,ei,J,er,N].concat((0,n.default)(K.map(t=>e[t])))},(n,l)=>{let r;return function({mark:e,code:n,underline:l,delete:r,strong:o,keyboard:a,italic:i},c){let s=c;function u(e,n){n&&(s=t.createElement(e,{},s))}return u("strong",o),u("u",l),u("del",r),u("code",n),u("mark",e),u("kbd",a),u("i",i),s}(e,t.createElement(t.Fragment,null,n.length>0&&l&&!eO&&eM?t.createElement("span",{key:"show-content","aria-hidden":!0},n):n,[(r=l)&&!eO&&t.createElement("span",{"aria-hidden":!0,key:"ellipsis"},"..."),ex.suffix,[r&&(()=>{let{expandable:e,symbol:n}=ex;return e?t.createElement("button",{type:"button",key:"expand",className:`${_}-${eO?"collapse":"expand"}`,onClick:e=>{var t,n;eE((t={expanded:!eO}).expanded),null==(n=ex.onExpand)||n.call(ex,e,t)},"aria-label":eO?N.collapse:null==N?void 0:N.expand},"function"==typeof n?n(eO):n):null})(),(()=>{if(!J)return;let{icon:e,tooltip:n,tabIndex:l}=Q,r=(0,s.default)(n)[0]||(null==N?void 0:N.edit),o="string"==typeof r?r:"";return ee.includes("icon")?t.createElement(y.default,{key:"edit",title:!1===n?"":r},t.createElement("button",{type:"button",ref:V,className:`${_}-edit`,onClick:el,"aria-label":o,tabIndex:l},e||t.createElement(a,{role:"button"}))):null})(),er?t.createElement(W,Object.assign({key:"copy"},eo,{prefixCls:_,copied:ea,locale:N,onCopy:ec,loading:ei,iconOnly:null==j})):null]]))}))))});var _=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let G=t.forwardRef((e,n)=>{let{ellipsis:l,rel:r,children:o,navigate:a}=e,i=_(e,["ellipsis","rel","children","navigate"]),c=Object.assign(Object.assign({},i),{rel:void 0===r&&"_blank"===i.target?"noopener noreferrer":r});return t.createElement(V,Object.assign({},c,{ref:n,ellipsis:!!l,component:"a"}),o)});var J=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Q=t.forwardRef((e,n)=>{let{children:l}=e,r=J(e,["children"]);return t.createElement(V,Object.assign({ref:n},r,{component:"div"}),l)});var Y=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Z=t.forwardRef((e,n)=>{let{ellipsis:l,children:r}=e,o=Y(e,["ellipsis","children"]),a=t.useMemo(()=>l&&"object"==typeof l?(0,p.default)(l,["expandable","rows"]):l,[l]);return t.createElement(V,Object.assign({ref:n},o,{ellipsis:a,component:"span"}),r)});var ee=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let et=[1,2,3,4,5],en=t.forwardRef((e,n)=>{let{level:l=1,children:r}=e,o=ee(e,["level","children"]),a=et.includes(l)?`h${l}`:"h1";return t.createElement(V,Object.assign({ref:n},o,{component:a}),r)});e.s(["default",0,en],335771),D.Text=Z,D.Link=G,D.Title=en,D.Paragraph=Q,e.s(["Typography",0,D],898586)}]); \ No newline at end of file + `]:{display:"inline-block",maxWidth:"100%"},"&-ellipsis-single-line":{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis","a&, span&":{verticalAlign:"bottom"},"> code":{paddingBlock:0,maxWidth:"calc(100% - 1.2em)",display:"inline-block",overflow:"hidden",textOverflow:"ellipsis",verticalAlign:"bottom",boxSizing:"content-box"}},"&-ellipsis-multiple-line":{display:"-webkit-box",overflow:"hidden",WebkitLineClamp:3,WebkitBoxOrient:"vertical"}}),{"&-rtl":{direction:"rtl"}})}},()=>({titleMarginTop:"1.2em",titleMarginBottom:"0.5em"})),k=e=>{let{prefixCls:n,"aria-label":l,className:r,style:o,direction:a,maxLength:c,autoSize:s=!0,value:u,onSave:d,onCancel:p,onEnd:f,component:m,enterIcon:g=t.createElement(h,null)}=e,b=t.useRef(null),y=t.useRef(!1),v=t.useRef(null),[w,S]=t.useState(u);t.useEffect(()=>{S(u)},[u]),t.useEffect(()=>{var e;if(null==(e=b.current)?void 0:e.resizableTextArea){let{textArea:e}=b.current.resizableTextArea;e.focus();let{length:t}=e.value;e.setSelectionRange(t,t)}},[]);let j=()=>{d(w.trim())},[k,R,$]=C(n),T=(0,i.default)(n,`${n}-edit-content`,{[`${n}-rtl`]:"rtl"===a,[`${n}-${m}`]:!!m},r,R,$);return k(t.createElement("div",{className:T,style:o},t.createElement(E.default,{ref:b,maxLength:c,value:w,onChange:({target:e})=>{S(e.value.replace(/[\n\r]/g,""))},onKeyDown:({keyCode:e})=>{y.current||(v.current=e)},onKeyUp:({keyCode:e,ctrlKey:t,altKey:n,metaKey:l,shiftKey:r})=>{v.current!==e||y.current||t||n||l||r||(e===x.default.ENTER?(j(),null==f||f()):e===x.default.ESC&&p())},onCompositionStart:()=>{y.current=!0},onCompositionEnd:()=>{y.current=!1},onBlur:()=>{j()},"aria-label":l,rows:1,autoSize:s}),null!==g?(0,O.cloneElement)(g,{className:`${n}-edit-content-confirm`}):null))};var R=e.i(844343),$=e.i(175066);function T(e,n){return t.useMemo(()=>{let t=!!e;return[t,Object.assign(Object.assign({},n),t&&"object"==typeof e?e:null)]},[e])}var I=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let D=t.forwardRef((e,n)=>{let{prefixCls:l,component:r="article",className:o,rootClassName:a,setContentRef:c,children:s,direction:u,style:d}=e,p=I(e,["prefixCls","component","className","rootClassName","setContentRef","children","direction","style"]),{getPrefixCls:m,direction:b,className:y,style:v}=(0,g.useComponentConfig)("typography"),h=c?(0,f.composeRef)(n,c):n,x=m("typography",l),[O,E,w]=C(x),S=(0,i.default)(x,y,{[`${x}-rtl`]:"rtl"===(null!=u?u:b)},o,a,E,w),j=Object.assign(Object.assign({},v),d);return O(t.createElement(r,Object.assign({className:S,style:j,ref:h},p),s))});var P=e.i(121229),H=e.i(190144),B=e.i(739295);function M(e){return!1===e?[!1,!1]:Array.isArray(e)?e:[e]}function z(e,t,n){return!0===e||void 0===e?t:e||n&&t}let A=e=>["string","number"].includes(typeof e),L=({prefixCls:e,copied:n,locale:l,iconOnly:r,tooltips:o,icon:a,tabIndex:c,onCopy:s,loading:u})=>{let d=M(o),p=M(a),{copied:f,copy:m}=null!=l?l:{},g=n?f:m,b=z(d[+!!n],g),v="string"==typeof b?b:g;return t.createElement(y.default,{title:b},t.createElement("button",{type:"button",className:(0,i.default)(`${e}-copy`,{[`${e}-copy-success`]:n,[`${e}-copy-icon-only`]:r}),onClick:s,"aria-label":v,tabIndex:c},n?z(p[1],t.createElement(P.default,null),!0):z(p[0],u?t.createElement(B.default,null):t.createElement(H.default,null),!0)))},W=t.forwardRef(({style:e,children:n},l)=>{let r=t.useRef(null);return t.useImperativeHandle(l,()=>({isExceed:()=>{let e=r.current;return e.scrollHeight>e.clientHeight},getHeight:()=>r.current.clientHeight})),t.createElement("span",{"aria-hidden":!0,ref:r,style:Object.assign({position:"fixed",display:"block",left:0,top:0,pointerEvents:"none",backgroundColor:"rgba(255, 0, 0, 0.65)"},e)},n)});function N(e,t){let n=0,l=[];for(let r=0;rt){let e=t-n;return l.push(String(o).slice(0,e)),l}l.push(o),n=a}return e}let U={display:"-webkit-box",overflow:"hidden",WebkitBoxOrient:"vertical"};function F(e){let{enableMeasure:l,width:r,text:o,children:a,rows:i,expanded:c,miscDeps:d,onEllipsis:p}=e,f=t.useMemo(()=>(0,s.default)(o),[o]),m=t.useMemo(()=>f.reduce((e,t)=>e+(A(t)?String(t).length:1),0),[o]),g=t.useMemo(()=>a(f,!1),[o]),[b,y]=t.useState(null),v=t.useRef(null),h=t.useRef(null),x=t.useRef(null),O=t.useRef(null),E=t.useRef(null),[w,S]=t.useState(!1),[j,C]=t.useState(0),[k,R]=t.useState(0),[$,T]=t.useState(null);(0,u.default)(()=>{l&&r&&m?C(1):C(0)},[r,o,i,l,f]),(0,u.default)(()=>{var e,t,n,l;if(1===j)C(2),T(h.current&&getComputedStyle(h.current).whiteSpace);else if(2===j){let r=!!(null==(e=x.current)?void 0:e.isExceed());C(r?3:4),y(r?[0,m]:null),S(r),R(Math.max((null==(t=x.current)?void 0:t.getHeight())||0,(1===i?0:(null==(n=O.current)?void 0:n.getHeight())||0)+((null==(l=E.current)?void 0:l.getHeight())||0))+1),p(r)}},[j]);let I=b?Math.ceil((b[0]+b[1])/2):0;(0,u.default)(()=>{var e;let[t,n]=b||[0,0];if(t!==n){let l=((null==(e=v.current)?void 0:e.getHeight())||0)>k,r=I;n-t==1&&(r=l?t:n),y(l?[t,r]:[r,n])}},[b,I]);let D=t.useMemo(()=>{if(!l)return a(f,!1);if(3!==j||!b||b[0]!==b[1]){let e=a(f,!1);return[4,0].includes(j)?e:t.createElement("span",{style:Object.assign(Object.assign({},U),{WebkitLineClamp:i})},e)}return a(c?f:N(f,b[0]),w)},[c,j,b,f].concat((0,n.default)(d))),P={width:r,margin:0,padding:0,whiteSpace:"nowrap"===$?"normal":"inherit"};return t.createElement(t.Fragment,null,D,2===j&&t.createElement(t.Fragment,null,t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i}),ref:x},g),t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i-1}),ref:O},g),t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:1}),ref:E},a([],!0))),3===j&&b&&b[0]!==b[1]&&t.createElement(W,{style:Object.assign(Object.assign({},P),{top:400}),ref:v},a(N(f,I),!0)),1===j&&t.createElement("span",{style:{whiteSpace:"inherit"},ref:h}))}let q=({enableEllipsis:e,isEllipsis:n,children:l,tooltipProps:r})=>(null==r?void 0:r.title)&&e?t.createElement(y.default,Object.assign({open:!!n&&void 0},r),l):l;var V=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let X=["delete","mark","code","underline","strong","keyboard","italic"],K=t.forwardRef((e,l)=>{var r;let o,v,h,{prefixCls:x,className:O,style:E,type:w,disabled:S,children:j,ellipsis:C,editable:I,copyable:P,component:H,title:B}=e,M=V(e,["prefixCls","className","style","type","disabled","children","ellipsis","editable","copyable","component","title"]),{getPrefixCls:z,direction:W}=t.useContext(g.ConfigContext),[N]=(0,b.default)("Text"),U=t.useRef(null),K=t.useRef(null),_=z("typography",x),G=(0,p.default)(M,X),[J,Q]=T(I),[Y,Z]=(0,d.default)(!1,{value:Q.editing}),{triggerType:ee=["icon"]}=Q,et=e=>{var t;e&&(null==(t=Q.onStart)||t.call(Q)),Z(e)},en=(o=(0,t.useRef)(void 0),(0,t.useEffect)(()=>{o.current=Y}),o.current);(0,u.default)(()=>{var e;!Y&&en&&(null==(e=K.current)||e.focus())},[Y]);let el=e=>{null==e||e.preventDefault(),et(!0)},[er,eo]=T(P),{copied:ea,copyLoading:ei,onClick:ec}=(({copyConfig:e,children:n})=>{let[l,r]=t.useState(!1),[o,a]=t.useState(!1),i=t.useRef(null),c=()=>{i.current&&clearTimeout(i.current)},s={};e.format&&(s.format=e.format),t.useEffect(()=>c,[]);let u=(0,$.default)(t=>{var l,o,u,d;return l=void 0,o=void 0,u=void 0,d=function*(){var l;null==t||t.preventDefault(),null==t||t.stopPropagation(),a(!0);try{let o="function"==typeof e.text?yield e.text():e.text;(0,R.default)(o||((e,t=!1)=>t&&null==e?[]:Array.isArray(e)?e:[e])(n,!0).join("")||"",s),a(!1),r(!0),c(),i.current=setTimeout(()=>{r(!1)},3e3),null==(l=e.onCopy)||l.call(e,t)}catch(e){throw a(!1),e}},new(u||(u=Promise))(function(e,t){function n(e){try{a(d.next(e))}catch(e){t(e)}}function r(e){try{a(d.throw(e))}catch(e){t(e)}}function a(t){var l;t.done?e(t.value):((l=t.value)instanceof u?l:new u(function(e){e(l)})).then(n,r)}a((d=d.apply(l,o||[])).next())})});return{copied:l,copyLoading:o,onClick:u}})({copyConfig:eo,children:j}),[es,eu]=t.useState(!1),[ed,ep]=t.useState(!1),[ef,em]=t.useState(!1),[eg,eb]=t.useState(!1),[ey,ev]=t.useState(!0),[eh,ex]=T(C,{expandable:!1,symbol:e=>e?null==N?void 0:N.collapse:null==N?void 0:N.expand}),[eO,eE]=(0,d.default)(ex.defaultExpanded||!1,{value:ex.expanded}),ew=eh&&(!eO||"collapsible"===ex.expandable),{rows:eS=1}=ex,ej=t.useMemo(()=>ew&&(void 0!==ex.suffix||ex.onEllipsis||ex.expandable||J||er),[ew,ex,J,er]);(0,u.default)(()=>{eh&&!ej&&(eu((0,m.isStyleSupport)("webkitLineClamp")),ep((0,m.isStyleSupport)("textOverflow")))},[ej,eh]);let[eC,ek]=t.useState(ew),eR=t.useMemo(()=>!ej&&(1===eS?ed:es),[ej,ed,es]);(0,u.default)(()=>{ek(eR&&ew)},[eR,ew]);let e$=ew&&(eC?eg:ef),eT=ew&&1===eS&&eC,eI=ew&&eS>1&&eC,[eD,eP]=t.useState(0),eH=e=>{var t;em(e),ef!==e&&(null==(t=ex.onEllipsis)||t.call(ex,e))};t.useEffect(()=>{let e=U.current;if(eh&&eC&&e){let t,n,l,r=(t=document.createElement("em"),e.appendChild(t),n=e.getBoundingClientRect(),l=t.getBoundingClientRect(),e.removeChild(t),n.left>l.left||l.right>n.right||n.top>l.top||l.bottom>n.bottom);eg!==r&&eb(r)}},[eh,eC,j,eI,ey,eD]),t.useEffect(()=>{let e=U.current;if("u"{ev(!!e.offsetParent)});return t.observe(e),()=>{t.disconnect()}},[eC,ew]);let eB=(v=ex.tooltip,h=Q.text,(0,t.useMemo)(()=>!0===v?{title:null!=h?h:j}:(0,t.isValidElement)(v)?{title:v}:"object"==typeof v?Object.assign({title:null!=h?h:j},v):{title:v},[v,h,j])),eM=t.useMemo(()=>{if(eh&&!eC)return[Q.text,j,B,eB.title].find(A)},[eh,eC,B,eB.title,e$]);return Y?t.createElement(k,{value:null!=(r=Q.text)?r:"string"==typeof j?j:"",onSave:e=>{var t;null==(t=Q.onChange)||t.call(Q,e),et(!1)},onCancel:()=>{var e;null==(e=Q.onCancel)||e.call(Q),et(!1)},onEnd:Q.onEnd,prefixCls:_,className:O,style:E,direction:W,component:H,maxLength:Q.maxLength,autoSize:Q.autoSize,enterIcon:Q.enterIcon}):t.createElement(c.default,{onResize:({offsetWidth:e})=>{eP(e)},disabled:!ew},r=>t.createElement(q,{tooltipProps:eB,enableEllipsis:ew,isEllipsis:e$},t.createElement(D,Object.assign({className:(0,i.default)({[`${_}-${w}`]:w,[`${_}-disabled`]:S,[`${_}-ellipsis`]:eh,[`${_}-ellipsis-single-line`]:eT,[`${_}-ellipsis-multiple-line`]:eI},O),prefixCls:x,style:Object.assign(Object.assign({},E),{WebkitLineClamp:eI?eS:void 0}),component:H,ref:(0,f.composeRef)(r,U,l),direction:W,onClick:ee.includes("text")?el:void 0,"aria-label":null==eM?void 0:eM.toString(),title:B},G),t.createElement(F,{enableMeasure:ew&&!eC,text:j,rows:eS,width:eD,onEllipsis:eH,expanded:eO,miscDeps:[ea,eO,ei,J,er,N].concat((0,n.default)(X.map(t=>e[t])))},(n,l)=>{let r;return function({mark:e,code:n,underline:l,delete:r,strong:o,keyboard:a,italic:i},c){let s=c;function u(e,n){n&&(s=t.createElement(e,{},s))}return u("strong",o),u("u",l),u("del",r),u("code",n),u("mark",e),u("kbd",a),u("i",i),s}(e,t.createElement(t.Fragment,null,n.length>0&&l&&!eO&&eM?t.createElement("span",{key:"show-content","aria-hidden":!0},n):n,[(r=l)&&!eO&&t.createElement("span",{"aria-hidden":!0,key:"ellipsis"},"..."),ex.suffix,[r&&(()=>{let{expandable:e,symbol:n}=ex;return e?t.createElement("button",{type:"button",key:"expand",className:`${_}-${eO?"collapse":"expand"}`,onClick:e=>{var t,n;eE((t={expanded:!eO}).expanded),null==(n=ex.onExpand)||n.call(ex,e,t)},"aria-label":eO?N.collapse:null==N?void 0:N.expand},"function"==typeof n?n(eO):n):null})(),(()=>{if(!J)return;let{icon:e,tooltip:n,tabIndex:l}=Q,r=(0,s.default)(n)[0]||(null==N?void 0:N.edit),o="string"==typeof r?r:"";return ee.includes("icon")?t.createElement(y.default,{key:"edit",title:!1===n?"":r},t.createElement("button",{type:"button",ref:K,className:`${_}-edit`,onClick:el,"aria-label":o,tabIndex:l},e||t.createElement(a,{role:"button"}))):null})(),er?t.createElement(L,Object.assign({key:"copy"},eo,{prefixCls:_,copied:ea,locale:N,onCopy:ec,loading:ei,iconOnly:null==j})):null]]))}))))});var _=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let G=t.forwardRef((e,n)=>{let{ellipsis:l,rel:r,children:o,navigate:a}=e,i=_(e,["ellipsis","rel","children","navigate"]),c=Object.assign(Object.assign({},i),{rel:void 0===r&&"_blank"===i.target?"noopener noreferrer":r});return t.createElement(K,Object.assign({},c,{ref:n,ellipsis:!!l,component:"a"}),o)});var J=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Q=t.forwardRef((e,n)=>{let{children:l}=e,r=J(e,["children"]);return t.createElement(K,Object.assign({ref:n},r,{component:"div"}),l)});var Y=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Z=t.forwardRef((e,n)=>{let{ellipsis:l,children:r}=e,o=Y(e,["ellipsis","children"]),a=t.useMemo(()=>l&&"object"==typeof l?(0,p.default)(l,["expandable","rows"]):l,[l]);return t.createElement(K,Object.assign({ref:n},o,{ellipsis:a,component:"span"}),r)});var ee=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let et=[1,2,3,4,5],en=t.forwardRef((e,n)=>{let{level:l=1,children:r}=e,o=ee(e,["level","children"]),a=et.includes(l)?`h${l}`:"h1";return t.createElement(K,Object.assign({ref:n},o,{component:a}),r)});e.s(["default",0,en],335771),D.Text=Z,D.Link=G,D.Title=en,D.Paragraph=Q,e.s(["Typography",0,D],898586)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.zblsr85hcyn.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.zblsr85hcyn.js deleted file mode 100644 index db300569099..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0.zblsr85hcyn.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,991124,e=>{"use strict";let t=(0,e.i(475254).default)("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);e.s(["default",0,t])},269200,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),a=i.default.forwardRef((e,a)=>{let{children:n,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement("div",{className:(0,r.tremorTwMerge)(o("root"),"overflow-auto",l)},i.default.createElement("table",Object.assign({ref:a,className:(0,r.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),n))});a.displayName="Table",e.s(["Table",0,a],269200)},942232,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),a=i.default.forwardRef((e,a)=>{let{children:n,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("tbody",Object.assign({ref:a,className:(0,r.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",l)},s),n))});a.displayName="TableBody",e.s(["TableBody",0,a],942232)},977572,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),a=i.default.forwardRef((e,a)=>{let{children:n,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("td",Object.assign({ref:a,className:(0,r.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",l)},s),n))});a.displayName="TableCell",e.s(["TableCell",0,a],977572)},427612,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),a=i.default.forwardRef((e,a)=>{let{children:n,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("thead",Object.assign({ref:a,className:(0,r.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",l)},s),n))});a.displayName="TableHead",e.s(["TableHead",0,a],427612)},64848,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),a=i.default.forwardRef((e,a)=>{let{children:n,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("th",Object.assign({ref:a,className:(0,r.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",l)},s),n))});a.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,a],64848)},496020,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),a=i.default.forwardRef((e,a)=>{let{children:n,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("tr",Object.assign({ref:a,className:(0,r.tremorTwMerge)(o("row"),l)},s),n))});a.displayName="TableRow",e.s(["TableRow",0,a],496020)},68155,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,i],68155)},360820,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,i],360820)},871943,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,i],871943)},389083,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(829087),o=e.i(480731),a=e.i(95779),n=e.i(444755),l=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,l.makeClassName)("Badge"),m=i.default.forwardRef((e,m)=>{let{color:g,icon:u,size:p=o.Sizes.SM,tooltip:h,className:f,children:b}=e,v=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),$=u||null,{tooltipProps:A,getReferenceProps:C}=(0,r.useTooltip)();return i.default.createElement("span",Object.assign({ref:(0,l.mergeRefs)([m,A.refs.setReference]),className:(0,n.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",g?(0,n.tremorTwMerge)((0,l.getColorClassNames)(g,a.colorPalette.background).bgColor,(0,l.getColorClassNames)(g,a.colorPalette.iconText).textColor,(0,l.getColorClassNames)(g,a.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[p].paddingX,s[p].paddingY,s[p].fontSize,f)},C,v),i.default.createElement(r.default,Object.assign({text:h},A)),$?i.default.createElement($,{className:(0,n.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,i.default.createElement("span",{className:(0,n.tremorTwMerge)(d("text"),"whitespace-nowrap")},b))});m.displayName="Badge",e.s(["Badge",0,m],389083)},94629,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,i],94629)},591935,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,i],591935)},728889,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(829087),o=e.i(480731),a=e.i(444755),n=e.i(673706),l=e.i(95779);let s={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},c={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},d={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},m=(0,n.makeClassName)("Icon"),g=i.default.forwardRef((e,g)=>{let{icon:u,variant:p="simple",tooltip:h,size:f=o.Sizes.SM,color:b,className:v}=e,$=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),A=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,n.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,n.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,n.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,n.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,n.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,n.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,a.tremorTwMerge)((0,n.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,n.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,n.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,n.getColorClassNames)(t,l.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,a.tremorTwMerge)((0,n.getColorClassNames)(t,l.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,b),{tooltipProps:C,getReferenceProps:I}=(0,r.useTooltip)();return i.default.createElement("span",Object.assign({ref:(0,n.mergeRefs)([g,C.refs.setReference]),className:(0,a.tremorTwMerge)(m("root"),"inline-flex shrink-0 items-center justify-center",A.bgColor,A.textColor,A.borderColor,A.ringColor,d[p].rounded,d[p].border,d[p].shadow,d[p].ring,s[f].paddingX,s[f].paddingY,v)},I,$),i.default.createElement(r.default,Object.assign({text:h},C)),i.default.createElement(u,{className:(0,a.tremorTwMerge)(m("icon"),"shrink-0",c[f].height,c[f].width)}))});g.displayName="Icon",e.s(["default",0,g],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},207670,e=>{"use strict";function t(){for(var e,t,i=0,r="",o=arguments.length;i{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,i],278587)},551332,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,i],551332)},122577,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,i],122577)},434626,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,i],434626)},902555,e=>{"use strict";var t=e.i(843476),i=e.i(591935),r=e.i(122577),o=e.i(278587),a=e.i(68155),n=e.i(360820),l=e.i(871943),s=e.i(434626),c=e.i(551332),d=e.i(592968),m=e.i(115504),g=e.i(752978);function u({icon:e,onClick:i,className:r,disabled:o,dataTestId:a}){return o?(0,t.jsx)(g.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,t.jsx)(g.Icon,{icon:e,size:"sm",onClick:i,className:(0,m.cx)("cursor-pointer",r),"data-testid":a})}let p={Edit:{icon:i.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:r.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:o.RefreshIcon,className:"hover:text-green-600"},Up:{icon:n.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:l.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:s.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:c.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:i,disabled:r=!1,disabledTooltipText:o,dataTestId:a,variant:n}){let{icon:l,className:s}=p[n];return(0,t.jsx)(d.Tooltip,{title:r?o:i,children:(0,t.jsx)("span",{children:(0,t.jsx)(u,{icon:l,onClick:e,className:s,disabled:r,dataTestId:a})})})}],902555)},755151,e=>{"use strict";var t=e.i(247153);e.s(["DownOutlined",()=>t.default])},916925,e=>{"use strict";var t,i=e.i(555987),r=((t={}).A2A_Agent="A2A Agent",t.AI21="Ai21",t.AI21_CHAT="Ai21 Chat",t.AIML="AI/ML API",t.AIOHTTP_OPENAI="Aiohttp Openai",t.Anthropic="Anthropic",t.ANTHROPIC_TEXT="Anthropic Text",t.AssemblyAI="AssemblyAI",t.AUTO_ROUTER="Auto Router",t.Bedrock="Amazon Bedrock",t.BedrockMantle="Amazon Bedrock Mantle",t.SageMaker="AWS SageMaker",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Foundry (Studio)",t.AZURE_TEXT="Azure Text",t.BASETEN="Baseten",t.BYTEZ="Bytez",t.Cerebras="Cerebras",t.CLARIFAI="Clarifai",t.CLOUDFLARE="Cloudflare",t.CODESTRAL="Codestral",t.Cohere="Cohere",t.COHERE_CHAT="Cohere Chat",t.COMETAPI="Cometapi",t.COMPACTIFAI="Compactifai",t.Cursor="Cursor",t.Dashscope="Dashscope",t.Databricks="Databricks (Qwen API)",t.DATAROBOT="Datarobot",t.DeepInfra="DeepInfra",t.Deepgram="Deepgram",t.Deepseek="Deepseek",t.DOCKER_MODEL_RUNNER="Docker Model Runner",t.DOTPROMPT="Dotprompt",t.ElevenLabs="ElevenLabs",t.EMPOWER="Empower",t.FalAI="Fal AI",t.FEATHERLESS_AI="Featherless Ai",t.FireworksAI="Fireworks AI",t.FRIENDLIAI="Friendliai",t.GALADRIEL="Galadriel",t.GITHUB_COPILOT="Github Copilot",t.Google_AI_Studio="Google AI Studio",t.GradientAI="GradientAI",t.Groq="Groq",t.HEROKU="Heroku",t.Hosted_Vllm="vllm",t.HUGGINGFACE="Huggingface",t.HYPERBOLIC="Hyperbolic",t.Infinity="Infinity",t.JinaAI="Jina AI",t.LAMBDA_AI="Lambda Ai",t.LEMONADE="Lemonade",t.LLAMAFILE="Llamafile",t.LM_STUDIO="Lm Studio",t.LLAMA="Meta Llama",t.MARITALK="Maritalk",t.MiniMax="MiniMax",t.MistralAI="Mistral AI",t.MOONSHOT="Moonshot",t.MORPH="Morph",t.NEBIUS="Nebius",t.NLP_CLOUD="Nlp Cloud",t.NOVITA="Novita",t.NSCALE="Nscale",t.NVIDIA_NIM="Nvidia Nim",t.Ollama="Ollama",t.OLLAMA_CHAT="Ollama Chat",t.OOBABOOGA="Oobabooga",t.OpenAI="OpenAI",t.OPENAI_LIKE="Openai Like",t.OpenAI_Compatible="OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)",t.OpenAI_Text="OpenAI Text Completion",t.OpenAI_Text_Compatible="OpenAI-Compatible Completions (legacy /v1/completions)",t.Openrouter="Openrouter",t.Oracle="Oracle Cloud Infrastructure (OCI)",t.OVHCLOUD="Ovhcloud",t.Perplexity="Perplexity",t.PETALS="Petals",t.PG_VECTOR="Pg Vector",t.PREDIBASE="Predibase",t.RECRAFT="Recraft",t.REPLICATE="Replicate",t.RunwayML="RunwayML",t.SAGEMAKER_LEGACY="Sagemaker",t.Sambanova="Sambanova",t.SAP="SAP Generative AI Hub",t.Snowflake="Snowflake",t.Soniox="Soniox",t.TEXT_COMPLETION_CODESTRAL="Text-Completion-Codestral",t.TogetherAI="TogetherAI",t.TOPAZ="Topaz",t.Triton="Triton",t.V0="V0",t.VERCEL_AI_GATEWAY="Vercel Ai Gateway",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.VERTEX_AI_BETA="Vertex Ai Beta",t.VLLM="Vllm",t.VolcEngine="VolcEngine",t.Voyage="Voyage AI",t.WANDB="Wandb",t.WATSONX="Watsonx",t.WATSONX_TEXT="Watsonx Text",t.xAI="xAI",t.XINFERENCE="Xinference",t.ZAI="Z.AI (Zhipu AI)",t);let o={A2A_Agent:"a2a_agent",AI21:"ai21",AI21_CHAT:"ai21_chat",AIML:"aiml",AIOHTTP_OPENAI:"aiohttp_openai",Anthropic:"anthropic",ANTHROPIC_TEXT:"anthropic_text",AssemblyAI:"assemblyai",AUTO_ROUTER:"auto_router",Azure:"azure",Azure_AI_Studio:"azure_ai",AZURE_TEXT:"azure_text",BASETEN:"baseten",Bedrock:"bedrock",BedrockMantle:"bedrock_mantle",BYTEZ:"bytez",Cerebras:"cerebras",CLARIFAI:"clarifai",CLOUDFLARE:"cloudflare",CODESTRAL:"codestral",Cohere:"cohere",COHERE_CHAT:"cohere_chat",COMETAPI:"cometapi",COMPACTIFAI:"compactifai",Cursor:"cursor",Dashscope:"dashscope",Databricks:"databricks",DATAROBOT:"datarobot",DeepInfra:"deepinfra",Deepgram:"deepgram",Deepseek:"deepseek",DOCKER_MODEL_RUNNER:"docker_model_runner",DOTPROMPT:"dotprompt",ElevenLabs:"elevenlabs",EMPOWER:"empower",FalAI:"fal_ai",FEATHERLESS_AI:"featherless_ai",FireworksAI:"fireworks_ai",FRIENDLIAI:"friendliai",GALADRIEL:"galadriel",GITHUB_COPILOT:"github_copilot",Google_AI_Studio:"gemini",GradientAI:"gradient_ai",Groq:"groq",HEROKU:"heroku",Hosted_Vllm:"hosted_vllm",HUGGINGFACE:"huggingface",HYPERBOLIC:"hyperbolic",Infinity:"infinity",JinaAI:"jina_ai",LAMBDA_AI:"lambda_ai",LEMONADE:"lemonade",LLAMAFILE:"llamafile",LLAMA:"meta_llama",LM_STUDIO:"lm_studio",MARITALK:"maritalk",MiniMax:"minimax",MistralAI:"mistral",MOONSHOT:"moonshot",MORPH:"morph",NEBIUS:"nebius",NLP_CLOUD:"nlp_cloud",NOVITA:"novita",NSCALE:"nscale",NVIDIA_NIM:"nvidia_nim",Ollama:"ollama",OLLAMA_CHAT:"ollama_chat",OOBABOOGA:"oobabooga",OpenAI:"openai",OPENAI_LIKE:"openai_like",OpenAI_Compatible:"openai",OpenAI_Text:"text-completion-openai",OpenAI_Text_Compatible:"text-completion-openai",Openrouter:"openrouter",Oracle:"oci",OVHCLOUD:"ovhcloud",Perplexity:"perplexity",PETALS:"petals",PG_VECTOR:"pg_vector",PREDIBASE:"predibase",RECRAFT:"recraft",REPLICATE:"replicate",RunwayML:"runwayml",SAGEMAKER_LEGACY:"sagemaker",SageMaker:"sagemaker_chat",Sambanova:"sambanova",SAP:"sap",Snowflake:"snowflake",Soniox:"soniox",TEXT_COMPLETION_CODESTRAL:"text-completion-codestral",TogetherAI:"together_ai",TOPAZ:"topaz",Triton:"triton",V0:"v0",VERCEL_AI_GATEWAY:"vercel_ai_gateway",Vertex_AI:"vertex_ai",VERTEX_AI_BETA:"vertex_ai_beta",VLLM:"vllm",VolcEngine:"volcengine",Voyage:"voyage",WANDB:"wandb",WATSONX:"watsonx",WATSONX_TEXT:"watsonx_text",xAI:"xai",XINFERENCE:"xinference",ZAI:"zai"},a=new Set(["bedrock_mantle"]),n="/ui/assets/logos/",l={"A2A Agent":`${n}a2a_agent.png`,Ai21:`${n}ai21.svg`,"Ai21 Chat":`${n}ai21.svg`,"AI/ML API":`${n}aiml_api.svg`,"Aiohttp Openai":`${n}openai_small.svg`,Anthropic:`${n}anthropic.svg`,"Anthropic Text":`${n}anthropic.svg`,AssemblyAI:`${n}assemblyai_small.png`,Azure:`${n}microsoft_azure.svg`,"Azure AI Foundry (Studio)":`${n}microsoft_azure.svg`,"Azure Text":`${n}microsoft_azure.svg`,Baseten:`${n}baseten.svg`,"Amazon Bedrock":`${n}bedrock.svg`,"Amazon Bedrock Mantle":`${n}bedrock.svg`,"AWS SageMaker":`${n}bedrock.svg`,Cerebras:`${n}cerebras.svg`,Cloudflare:`${n}cloudflare.svg`,Codestral:`${n}mistral.svg`,Cohere:`${n}cohere.svg`,"Cohere Chat":`${n}cohere.svg`,Cometapi:`${n}cometapi.svg`,Cursor:`${n}cursor.svg`,"Databricks (Qwen API)":`${n}databricks.svg`,Dashscope:`${n}dashscope.svg`,Deepseek:`${n}deepseek.svg`,Deepgram:`${n}deepgram.png`,DeepInfra:`${n}deepinfra.png`,ElevenLabs:`${n}elevenlabs.png`,"Fal AI":`${n}fal_ai.jpg`,"Featherless Ai":`${n}featherless.svg`,"Fireworks AI":`${n}fireworks.svg`,Friendliai:`${n}friendli.svg`,"Github Copilot":`${n}github_copilot.svg`,"Google AI Studio":`${n}google.svg`,GradientAI:`${n}gradientai.svg`,Groq:`${n}groq.svg`,vllm:`${n}vllm.png`,Huggingface:`${n}huggingface.svg`,Hyperbolic:`${n}hyperbolic.svg`,Infinity:`${n}infinity.png`,"Jina AI":`${n}jina.png`,"Lambda Ai":`${n}lambda.svg`,"Lm Studio":`${n}lmstudio.svg`,"Meta Llama":`${n}meta_llama.svg`,MiniMax:`${n}minimax.svg`,"Mistral AI":`${n}mistral.svg`,Moonshot:`${n}moonshot.svg`,Morph:`${n}morph.svg`,Nebius:`${n}nebius.svg`,Novita:`${n}novita.svg`,"Nvidia Nim":`${n}nvidia_nim.svg`,Ollama:`${n}ollama.svg`,"Ollama Chat":`${n}ollama.svg`,Oobabooga:`${n}openai_small.svg`,OpenAI:`${n}openai_small.svg`,"Openai Like":`${n}openai_small.svg`,"OpenAI Text Completion":`${n}openai_small.svg`,"OpenAI-Compatible Completions (legacy /v1/completions)":`${n}openai_small.svg`,"OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)":`${n}openai_small.svg`,Openrouter:`${n}openrouter.svg`,"Oracle Cloud Infrastructure (OCI)":`${n}oracle.svg`,Perplexity:`${n}perplexity-ai.svg`,Recraft:`${n}recraft.svg`,Replicate:`${n}replicate.svg`,RunwayML:`${n}runwayml.png`,Sagemaker:`${n}bedrock.svg`,Sambanova:`${n}sambanova.svg`,"SAP Generative AI Hub":`${n}sap.png`,Snowflake:`${n}snowflake.svg`,Soniox:`${n}soniox.svg`,"Text-Completion-Codestral":`${n}mistral.svg`,TogetherAI:`${n}togetherai.svg`,Topaz:`${n}topaz.svg`,Triton:`${n}nvidia_triton.png`,V0:`${n}v0.svg`,"Vercel Ai Gateway":`${n}vercel.svg`,"Vertex AI (Anthropic, Gemini, etc.)":`${n}google.svg`,"Vertex Ai Beta":`${n}google.svg`,Vllm:`${n}vllm.png`,VolcEngine:`${n}volcengine.png`,"Voyage AI":`${n}voyage.webp`,Watsonx:`${n}watsonx.svg`,"Watsonx Text":`${n}watsonx.svg`,xAI:`${n}xai.svg`,Xinference:`${n}xinference.svg`};e.s(["Providers",()=>r,"getPlaceholder",0,e=>{if("AI/ML API"===e)return"aiml/flux-pro/v1.1";if("Vertex AI (Anthropic, Gemini, etc.)"===e)return"gemini-pro";if("Anthropic"==e)return"claude-3-opus";if("Amazon Bedrock"==e)return"claude-3-opus";if("AWS SageMaker"==e)return"sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b";else if("Google AI Studio"==e)return"gemini-pro";else if("Azure AI Foundry (Studio)"==e)return"azure_ai/command-r-plus";else if("Azure"==e)return"my-deployment";else if("Oracle Cloud Infrastructure (OCI)"==e)return"oci/xai.grok-4";else if("Snowflake"==e)return"snowflake/mistral-7b";else if("Voyage AI"==e)return"voyage/";else if("Jina AI"==e)return"jina_ai/";else if("VolcEngine"==e)return"volcengine/";else if("DeepInfra"==e)return"deepinfra/";else if("Fal AI"==e)return"fal_ai/fal-ai/flux-pro/v1.1-ultra";else if("RunwayML"==e)return"runwayml/gen4_turbo";else if("Watsonx"===e)return"watsonx/ibm/granite-3-3-8b-instruct";else if("Cursor"===e)return"cursor/claude-4-sonnet";else if("Z.AI (Zhipu AI)"===e)return"zai/glm-4.5";else return"gpt-3.5-turbo"},"getProviderLogoAndName",0,e=>{if(!e)return{logo:"",displayName:"-"};if("gemini"===e.toLowerCase()){let e="Google AI Studio";return{logo:(0,i.resolveLogoSrc)(l[e])??"",displayName:e}}let t=Object.keys(o).find(t=>o[t].toLowerCase()===e.toLowerCase())??Object.keys(o).find(t=>t.toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let a=r[t];return{logo:(0,i.resolveLogoSrc)(l[a])??"",displayName:a}},"getProviderModels",0,(e,t)=>{let i=o[e],r=[];return e&&"object"==typeof t&&(Object.entries(t).forEach(([e,t])=>{if(null!==t&&"object"==typeof t&&"litellm_provider"in t){let o=t.litellm_provider,n="string"==typeof o&&(o.startsWith(`${i}_`)||o.startsWith(`${i}-`));(o===i||n&&!a.has(o))&&r.push(e)}}),"Cohere"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere_chat"===t.litellm_provider&&r.push(e)}),"AWS SageMaker"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"sagemaker_chat"===t.litellm_provider&&r.push(e)})),r},"providerLogoMap",0,l,"provider_map",0,o])},928685,e=>{"use strict";var t=e.i(38953);e.s(["SearchOutlined",()=>t.default])},596239,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M574 665.4a8.03 8.03 0 00-11.3 0L446.5 781.6c-53.8 53.8-144.6 59.5-204 0-59.5-59.5-53.8-150.2 0-204l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3l-39.8-39.8a8.03 8.03 0 00-11.3 0L191.4 526.5c-84.6 84.6-84.6 221.5 0 306s221.5 84.6 306 0l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3L574 665.4zm258.6-474c-84.6-84.6-221.5-84.6-306 0L410.3 307.6a8.03 8.03 0 000 11.3l39.7 39.7c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c53.8-53.8 144.6-59.5 204 0 59.5 59.5 53.8 150.2 0 204L665.3 562.6a8.03 8.03 0 000 11.3l39.8 39.8c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c84.5-84.6 84.5-221.5 0-306.1zM610.1 372.3a8.03 8.03 0 00-11.3 0L372.3 598.7a8.03 8.03 0 000 11.3l39.6 39.6c3.1 3.1 8.2 3.1 11.3 0l226.4-226.4c3.1-3.1 3.1-8.2 0-11.3l-39.5-39.6z"}}]},name:"link",theme:"outlined"};var o=e.i(9583),a=i.forwardRef(function(e,a){return i.createElement(o.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["LinkOutlined",0,a],596239)},280898,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(121229),r=e.i(864517),o=e.i(343794),a=e.i(931067),n=e.i(209428),l=e.i(211577),s=e.i(703923),c=e.i(404948),d=["className","prefixCls","style","active","status","iconPrefix","icon","wrapperStyle","stepNumber","disabled","description","title","subTitle","progressDot","stepIcon","tailContent","icons","stepIndex","onStepClick","onClick","render"];function m(e){return"string"==typeof e}let g=function(e){var i,r,g,u,p,h=e.className,f=e.prefixCls,b=e.style,v=e.active,$=e.status,A=e.iconPrefix,C=e.icon,I=(e.wrapperStyle,e.stepNumber),w=e.disabled,x=e.description,k=e.title,S=e.subTitle,E=e.progressDot,T=e.stepIcon,O=e.tailContent,y=e.icons,N=e.stepIndex,L=e.onStepClick,_=e.onClick,M=e.render,R=(0,s.default)(e,d),P={};L&&!w&&(P.role="button",P.tabIndex=0,P.onClick=function(e){null==_||_(e),L(N)},P.onKeyDown=function(e){var t=e.which;(t===c.default.ENTER||t===c.default.SPACE)&&L(N)});var z=$||"wait",H=(0,o.default)("".concat(f,"-item"),"".concat(f,"-item-").concat(z),h,(p={},(0,l.default)(p,"".concat(f,"-item-custom"),C),(0,l.default)(p,"".concat(f,"-item-active"),v),(0,l.default)(p,"".concat(f,"-item-disabled"),!0===w),p)),j=(0,n.default)({},b),D=t.createElement("div",(0,a.default)({},R,{className:H,style:j}),t.createElement("div",(0,a.default)({onClick:_},P,{className:"".concat(f,"-item-container")}),t.createElement("div",{className:"".concat(f,"-item-tail")},O),t.createElement("div",{className:"".concat(f,"-item-icon")},(g=(0,o.default)("".concat(f,"-icon"),"".concat(A,"icon"),(i={},(0,l.default)(i,"".concat(A,"icon-").concat(C),C&&m(C)),(0,l.default)(i,"".concat(A,"icon-check"),!C&&"finish"===$&&(y&&!y.finish||!y)),(0,l.default)(i,"".concat(A,"icon-cross"),!C&&"error"===$&&(y&&!y.error||!y)),i)),u=t.createElement("span",{className:"".concat(f,"-icon-dot")}),r=E?"function"==typeof E?t.createElement("span",{className:"".concat(f,"-icon")},E(u,{index:I-1,status:$,title:k,description:x})):t.createElement("span",{className:"".concat(f,"-icon")},u):C&&!m(C)?t.createElement("span",{className:"".concat(f,"-icon")},C):y&&y.finish&&"finish"===$?t.createElement("span",{className:"".concat(f,"-icon")},y.finish):y&&y.error&&"error"===$?t.createElement("span",{className:"".concat(f,"-icon")},y.error):C||"finish"===$||"error"===$?t.createElement("span",{className:g}):t.createElement("span",{className:"".concat(f,"-icon")},I),T&&(r=T({index:I-1,status:$,title:k,description:x,node:r})),r)),t.createElement("div",{className:"".concat(f,"-item-content")},t.createElement("div",{className:"".concat(f,"-item-title")},k,S&&t.createElement("div",{title:"string"==typeof S?S:void 0,className:"".concat(f,"-item-subtitle")},S)),x&&t.createElement("div",{className:"".concat(f,"-item-description")},x))));return M&&(D=M(D)||null),D};var u=["prefixCls","style","className","children","direction","type","labelPlacement","iconPrefix","status","size","current","progressDot","stepIcon","initial","icons","onChange","itemRender","items"];function p(e){var i,r=e.prefixCls,c=void 0===r?"rc-steps":r,d=e.style,m=void 0===d?{}:d,p=e.className,h=(e.children,e.direction),f=e.type,b=void 0===f?"default":f,v=e.labelPlacement,$=e.iconPrefix,A=void 0===$?"rc":$,C=e.status,I=void 0===C?"process":C,w=e.size,x=e.current,k=void 0===x?0:x,S=e.progressDot,E=e.stepIcon,T=e.initial,O=void 0===T?0:T,y=e.icons,N=e.onChange,L=e.itemRender,_=e.items,M=(0,s.default)(e,u),R="inline"===b,P=R||void 0!==S&&S,z=R||void 0===h?"horizontal":h,H=R?void 0:w,j=(0,o.default)(c,"".concat(c,"-").concat(z),p,(i={},(0,l.default)(i,"".concat(c,"-").concat(H),H),(0,l.default)(i,"".concat(c,"-label-").concat(P?"vertical":void 0===v?"horizontal":v),"horizontal"===z),(0,l.default)(i,"".concat(c,"-dot"),!!P),(0,l.default)(i,"".concat(c,"-navigation"),"navigation"===b),(0,l.default)(i,"".concat(c,"-inline"),R),i)),D=function(e){N&&k!==e&&N(e)};return t.default.createElement("div",(0,a.default)({className:j,style:m},M),(void 0===_?[]:_).filter(function(e){return e}).map(function(e,i){var r=(0,n.default)({},e),o=O+i;return"error"===I&&i===k-1&&(r.className="".concat(c,"-next-error")),r.status||(o===k?r.status=I:o{let i=`${t.componentCls}-item`,r=`${e}IconColor`,o=`${e}TitleColor`,a=`${e}DescriptionColor`,n=`${e}TailColor`,l=`${e}IconBgColor`,s=`${e}IconBorderColor`,c=`${e}DotColor`;return{[`${i}-${e} ${i}-icon`]:{backgroundColor:t[l],borderColor:t[s],[`> ${t.componentCls}-icon`]:{color:t[r],[`${t.componentCls}-icon-dot`]:{background:t[c]}}},[`${i}-${e}${i}-custom ${i}-icon`]:{[`> ${t.componentCls}-icon`]:{color:t[c]}},[`${i}-${e} > ${i}-container > ${i}-content > ${i}-title`]:{color:t[o],"&::after":{backgroundColor:t[n]}},[`${i}-${e} > ${i}-container > ${i}-content > ${i}-description`]:{color:t[a]},[`${i}-${e} > ${i}-container > ${i}-tail::after`]:{backgroundColor:t[n]}}},k=(0,I.genStyleHooks)("Steps",e=>{let{colorTextDisabled:t,controlHeightLG:i,colorTextLightSolid:r,colorText:o,colorPrimary:a,colorTextDescription:n,colorTextQuaternary:l,colorError:s,colorBorderSecondary:c,colorSplit:d}=e;return(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,C.resetComponent)(e)),{display:"flex",width:"100%",fontSize:0,textAlign:"initial"}),(e=>{let{componentCls:t,motionDurationSlow:i}=e,r=`${t}-item`,o=`${r}-icon`;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:{position:"relative",display:"inline-block",flex:1,overflow:"hidden",verticalAlign:"top","&:last-child":{flex:"none",[`> ${r}-container > ${r}-tail, > ${r}-container > ${r}-content > ${r}-title::after`]:{display:"none"}}},[`${r}-container`]:{outline:"none",[`&:focus-visible ${o}`]:(0,C.genFocusOutline)(e)},[`${o}, ${r}-content`]:{display:"inline-block",verticalAlign:"top"},[o]:{width:e.iconSize,height:e.iconSize,marginTop:0,marginBottom:0,marginInlineStart:0,marginInlineEnd:e.marginXS,fontSize:e.iconFontSize,fontFamily:e.fontFamily,lineHeight:(0,A.unit)(e.iconSize),textAlign:"center",borderRadius:e.iconSize,border:`${(0,A.unit)(e.lineWidth)} ${e.lineType} transparent`,transition:`background-color ${i}, border-color ${i}`,[`${t}-icon`]:{position:"relative",top:e.iconTop,color:e.colorPrimary,lineHeight:1}},[`${r}-tail`]:{position:"absolute",top:e.calc(e.iconSize).div(2).equal(),insetInlineStart:0,width:"100%","&::after":{display:"inline-block",width:"100%",height:e.lineWidth,background:e.colorSplit,borderRadius:e.lineWidth,transition:`background ${i}`,content:'""'}},[`${r}-title`]:{position:"relative",display:"inline-block",paddingInlineEnd:e.padding,color:e.colorText,fontSize:e.fontSizeLG,lineHeight:(0,A.unit)(e.titleLineHeight),"&::after":{position:"absolute",top:e.calc(e.titleLineHeight).div(2).equal(),insetInlineStart:"100%",display:"block",width:9999,height:e.lineWidth,background:e.processTailColor,content:'""'}},[`${r}-subtitle`]:{display:"inline",marginInlineStart:e.marginXS,color:e.colorTextDescription,fontWeight:"normal",fontSize:e.fontSize},[`${r}-description`]:{color:e.colorTextDescription,fontSize:e.fontSize}},x("wait",e)),x("process",e)),{[`${r}-process > ${r}-container > ${r}-title`]:{fontWeight:e.fontWeightStrong}}),x("finish",e)),x("error",e)),{[`${r}${t}-next-error > ${t}-item-title::after`]:{background:e.colorError},[`${r}-disabled`]:{cursor:"not-allowed"}})})(e)),(e=>{let{componentCls:t,motionDurationSlow:i}=e;return{[`& ${t}-item`]:{[`&:not(${t}-item-active)`]:{[`& > ${t}-item-container[role='button']`]:{cursor:"pointer",[`${t}-item`]:{[`&-title, &-subtitle, &-description, &-icon ${t}-icon`]:{transition:`color ${i}`}},"&:hover":{[`${t}-item`]:{"&-title, &-subtitle, &-description":{color:e.colorPrimary}}}},[`&:not(${t}-item-process)`]:{[`& > ${t}-item-container[role='button']:hover`]:{[`${t}-item`]:{"&-icon":{borderColor:e.colorPrimary,[`${t}-icon`]:{color:e.colorPrimary}}}}}}},[`&${t}-horizontal:not(${t}-label-vertical)`]:{[`${t}-item`]:{paddingInlineStart:e.padding,whiteSpace:"nowrap","&:first-child":{paddingInlineStart:0},[`&:last-child ${t}-item-title`]:{paddingInlineEnd:0},"&-tail":{display:"none"},"&-description":{maxWidth:e.descriptionMaxWidth,whiteSpace:"normal"}}}}})(e)),(e=>{let{componentCls:t,customIconTop:i,customIconSize:r,customIconFontSize:o}=e;return{[`${t}-item-custom`]:{[`> ${t}-item-container > ${t}-item-icon`]:{height:"auto",background:"none",border:0,[`> ${t}-icon`]:{top:i,width:r,height:r,fontSize:o,lineHeight:(0,A.unit)(r)}}},[`&:not(${t}-vertical)`]:{[`${t}-item-custom`]:{[`${t}-item-icon`]:{width:"auto",background:"none"}}}}})(e)),(e=>{let{componentCls:t,iconSizeSM:i,fontSizeSM:r,fontSize:o,colorTextDescription:a}=e;return{[`&${t}-small`]:{[`&${t}-horizontal:not(${t}-label-vertical) ${t}-item`]:{paddingInlineStart:e.paddingSM,"&:first-child":{paddingInlineStart:0}},[`${t}-item-icon`]:{width:i,height:i,marginTop:0,marginBottom:0,marginInline:`0 ${(0,A.unit)(e.marginXS)}`,fontSize:r,lineHeight:(0,A.unit)(i),textAlign:"center",borderRadius:i},[`${t}-item-title`]:{paddingInlineEnd:e.paddingSM,fontSize:o,lineHeight:(0,A.unit)(i),"&::after":{top:e.calc(i).div(2).equal()}},[`${t}-item-description`]:{color:a,fontSize:o},[`${t}-item-tail`]:{top:e.calc(i).div(2).sub(e.paddingXXS).equal()},[`${t}-item-custom ${t}-item-icon`]:{width:"inherit",height:"inherit",lineHeight:"inherit",background:"none",border:0,borderRadius:0,[`> ${t}-icon`]:{fontSize:i,lineHeight:(0,A.unit)(i),transform:"none"}}}}})(e)),(e=>{let{componentCls:t,iconSizeSM:i,iconSize:r}=e;return{[`&${t}-vertical`]:{display:"flex",flexDirection:"column",[`> ${t}-item`]:{display:"block",flex:"1 0 auto",paddingInlineStart:0,overflow:"visible",[`${t}-item-icon`]:{float:"left",marginInlineEnd:e.margin},[`${t}-item-content`]:{display:"block",minHeight:e.calc(e.controlHeight).mul(1.5).equal(),overflow:"hidden"},[`${t}-item-title`]:{lineHeight:(0,A.unit)(r)},[`${t}-item-description`]:{paddingBottom:e.paddingSM}},[`> ${t}-item > ${t}-item-container > ${t}-item-tail`]:{position:"absolute",top:0,insetInlineStart:e.calc(r).div(2).sub(e.lineWidth).equal(),width:e.lineWidth,height:"100%",padding:`${(0,A.unit)(e.calc(e.marginXXS).mul(1.5).add(r).equal())} 0 ${(0,A.unit)(e.calc(e.marginXXS).mul(1.5).equal())}`,"&::after":{width:e.lineWidth,height:"100%"}},[`> ${t}-item:not(:last-child) > ${t}-item-container > ${t}-item-tail`]:{display:"block"},[` > ${t}-item > ${t}-item-container > ${t}-item-content > ${t}-item-title`]:{"&::after":{display:"none"}},[`&${t}-small ${t}-item-container`]:{[`${t}-item-tail`]:{position:"absolute",top:0,insetInlineStart:e.calc(i).div(2).sub(e.lineWidth).equal(),padding:`${(0,A.unit)(e.calc(e.marginXXS).mul(1.5).add(i).equal())} 0 ${(0,A.unit)(e.calc(e.marginXXS).mul(1.5).equal())}`},[`${t}-item-title`]:{lineHeight:(0,A.unit)(i)}}}}})(e)),(e=>{let{componentCls:t}=e,i=`${t}-item`;return{[`${t}-horizontal`]:{[`${i}-tail`]:{transform:"translateY(-50%)"}}}})(e)),(e=>{let{componentCls:t,iconSize:i,lineHeight:r,iconSizeSM:o}=e;return{[`&${t}-label-vertical`]:{[`${t}-item`]:{overflow:"visible","&-tail":{marginInlineStart:e.calc(i).div(2).add(e.controlHeightLG).equal(),padding:`0 ${(0,A.unit)(e.paddingLG)}`},"&-content":{display:"block",width:e.calc(i).div(2).add(e.controlHeightLG).mul(2).equal(),marginTop:e.marginSM,textAlign:"center"},"&-icon":{display:"inline-block",marginInlineStart:e.controlHeightLG},"&-title":{paddingInlineEnd:0,paddingInlineStart:0,"&::after":{display:"none"}},"&-subtitle":{display:"block",marginBottom:e.marginXXS,marginInlineStart:0,lineHeight:r}},[`&${t}-small:not(${t}-dot)`]:{[`${t}-item`]:{"&-icon":{marginInlineStart:e.calc(i).sub(o).div(2).add(e.controlHeightLG).equal()}}}}}})(e)),(e=>{let{componentCls:t,descriptionMaxWidth:i,lineHeight:r,dotCurrentSize:o,dotSize:a,motionDurationSlow:n}=e;return{[`&${t}-dot, &${t}-dot${t}-small`]:{[`${t}-item`]:{"&-title":{lineHeight:r},"&-tail":{top:e.calc(e.dotSize).sub(e.calc(e.lineWidth).mul(3).equal()).div(2).equal(),width:"100%",marginTop:0,marginBottom:0,marginInline:`${(0,A.unit)(e.calc(i).div(2).equal())} 0`,padding:0,"&::after":{width:`calc(100% - ${(0,A.unit)(e.calc(e.marginSM).mul(2).equal())})`,height:e.calc(e.lineWidth).mul(3).equal(),marginInlineStart:e.marginSM}},"&-icon":{width:a,height:a,marginInlineStart:e.calc(e.descriptionMaxWidth).sub(a).div(2).equal(),paddingInlineEnd:0,lineHeight:(0,A.unit)(a),background:"transparent",border:0,[`${t}-icon-dot`]:{position:"relative",float:"left",width:"100%",height:"100%",borderRadius:100,transition:`all ${n}`,"&::after":{position:"absolute",top:e.calc(e.marginSM).mul(-1).equal(),insetInlineStart:e.calc(a).sub(e.calc(e.controlHeightLG).mul(1.5).equal()).div(2).equal(),width:e.calc(e.controlHeightLG).mul(1.5).equal(),height:e.controlHeight,background:"transparent",content:'""'}}},"&-content":{width:i},[`&-process ${t}-item-icon`]:{position:"relative",top:e.calc(a).sub(o).div(2).equal(),width:o,height:o,lineHeight:(0,A.unit)(o),background:"none",marginInlineStart:e.calc(e.descriptionMaxWidth).sub(o).div(2).equal()},[`&-process ${t}-icon`]:{[`&:first-child ${t}-icon-dot`]:{insetInlineStart:0}}}},[`&${t}-vertical${t}-dot`]:{[`${t}-item-icon`]:{marginTop:e.calc(e.controlHeight).sub(a).div(2).equal(),marginInlineStart:0,background:"none"},[`${t}-item-process ${t}-item-icon`]:{marginTop:e.calc(e.controlHeight).sub(o).div(2).equal(),top:0,insetInlineStart:e.calc(a).sub(o).div(2).equal(),marginInlineStart:0},[`${t}-item > ${t}-item-container > ${t}-item-tail`]:{top:e.calc(e.controlHeight).sub(a).div(2).equal(),insetInlineStart:0,margin:0,padding:`${(0,A.unit)(e.calc(a).add(e.paddingXS).equal())} 0 ${(0,A.unit)(e.paddingXS)}`,"&::after":{marginInlineStart:e.calc(a).sub(e.lineWidth).div(2).equal()}},[`&${t}-small`]:{[`${t}-item-icon`]:{marginTop:e.calc(e.controlHeightSM).sub(a).div(2).equal()},[`${t}-item-process ${t}-item-icon`]:{marginTop:e.calc(e.controlHeightSM).sub(o).div(2).equal()},[`${t}-item > ${t}-item-container > ${t}-item-tail`]:{top:e.calc(e.controlHeightSM).sub(a).div(2).equal()}},[`${t}-item:first-child ${t}-icon-dot`]:{insetInlineStart:0},[`${t}-item-content`]:{width:"inherit"}}}})(e)),(e=>{let{componentCls:t,navContentMaxWidth:i,navArrowColor:r,stepsNavActiveColor:o,motionDurationSlow:a}=e;return{[`&${t}-navigation`]:{paddingTop:e.paddingSM,[`&${t}-small`]:{[`${t}-item`]:{"&-container":{marginInlineStart:e.calc(e.marginSM).mul(-1).equal()}}},[`${t}-item`]:{overflow:"visible",textAlign:"center","&-container":{display:"inline-block",height:"100%",marginInlineStart:e.calc(e.margin).mul(-1).equal(),paddingBottom:e.paddingSM,textAlign:"start",transition:`opacity ${a}`,[`${t}-item-content`]:{maxWidth:i},[`${t}-item-title`]:Object.assign(Object.assign({maxWidth:"100%",paddingInlineEnd:0},C.textEllipsis),{"&::after":{display:"none"}})},[`&:not(${t}-item-active)`]:{[`${t}-item-container[role='button']`]:{cursor:"pointer","&:hover":{opacity:.85}}},"&:last-child":{flex:1,"&::after":{display:"none"}},"&::after":{position:"absolute",top:`calc(50% - ${(0,A.unit)(e.calc(e.paddingSM).div(2).equal())})`,insetInlineStart:"100%",display:"inline-block",width:e.fontSizeIcon,height:e.fontSizeIcon,borderTop:`${(0,A.unit)(e.lineWidth)} ${e.lineType} ${r}`,borderBottom:"none",borderInlineStart:"none",borderInlineEnd:`${(0,A.unit)(e.lineWidth)} ${e.lineType} ${r}`,transform:"translateY(-50%) translateX(-50%) rotate(45deg)",content:'""'},"&::before":{position:"absolute",bottom:0,insetInlineStart:"50%",display:"inline-block",width:0,height:e.lineWidthBold,backgroundColor:o,transition:`width ${a}, inset-inline-start ${a}`,transitionTimingFunction:"ease-out",content:'""'}},[`${t}-item${t}-item-active::before`]:{insetInlineStart:0,width:"100%"}},[`&${t}-navigation${t}-vertical`]:{[`> ${t}-item`]:{marginInlineEnd:0,"&::before":{display:"none"},[`&${t}-item-active::before`]:{top:0,insetInlineEnd:0,insetInlineStart:"unset",display:"block",width:e.calc(e.lineWidth).mul(3).equal(),height:`calc(100% - ${(0,A.unit)(e.marginLG)})`},"&::after":{position:"relative",insetInlineStart:"50%",display:"block",width:e.calc(e.controlHeight).mul(.25).equal(),height:e.calc(e.controlHeight).mul(.25).equal(),marginBottom:e.marginXS,textAlign:"center",transform:"translateY(-50%) translateX(-50%) rotate(135deg)"},"&:last-child":{"&::after":{display:"none"}},[`> ${t}-item-container > ${t}-item-tail`]:{visibility:"hidden"}}},[`&${t}-navigation${t}-horizontal`]:{[`> ${t}-item > ${t}-item-container > ${t}-item-tail`]:{visibility:"hidden"}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-rtl`]:{direction:"rtl",[`${t}-item`]:{"&-subtitle":{float:"left"}},[`&${t}-navigation`]:{[`${t}-item::after`]:{transform:"rotate(-45deg)"}},[`&${t}-vertical`]:{[`> ${t}-item`]:{"&::after":{transform:"rotate(225deg)"},[`${t}-item-icon`]:{float:"right"}}},[`&${t}-dot`]:{[`${t}-item-icon ${t}-icon-dot, &${t}-small ${t}-item-icon ${t}-icon-dot`]:{float:"right"}}}}})(e)),(e=>{let{antCls:t,componentCls:i,iconSize:r,iconSizeSM:o,processIconColor:a,marginXXS:n,lineWidthBold:l,lineWidth:s,paddingXXS:c}=e,d=e.calc(r).add(e.calc(l).mul(4).equal()).equal(),m=e.calc(o).add(e.calc(e.lineWidth).mul(4).equal()).equal();return{[`&${i}-with-progress`]:{[`${i}-item`]:{paddingTop:c,[`&-process ${i}-item-container ${i}-item-icon ${i}-icon`]:{color:a}},[`&${i}-vertical > ${i}-item `]:{paddingInlineStart:c,[`> ${i}-item-container > ${i}-item-tail`]:{top:n,insetInlineStart:e.calc(r).div(2).sub(s).add(c).equal()}},[`&, &${i}-small`]:{[`&${i}-horizontal ${i}-item:first-child`]:{paddingBottom:c,paddingInlineStart:c}},[`&${i}-small${i}-vertical > ${i}-item > ${i}-item-container > ${i}-item-tail`]:{insetInlineStart:e.calc(o).div(2).sub(s).add(c).equal()},[`&${i}-label-vertical ${i}-item ${i}-item-tail`]:{top:e.calc(r).div(2).add(c).equal()},[`${i}-item-icon`]:{position:"relative",[`${t}-progress`]:{position:"absolute",insetInlineStart:"50%",top:"50%",transform:"translate(-50%, -50%)","&-inner":{width:`${(0,A.unit)(d)} !important`,height:`${(0,A.unit)(d)} !important`}}},[`&${i}-small`]:{[`&${i}-label-vertical ${i}-item ${i}-item-tail`]:{top:e.calc(o).div(2).add(c).equal()},[`${i}-item-icon ${t}-progress-inner`]:{width:`${(0,A.unit)(m)} !important`,height:`${(0,A.unit)(m)} !important`}}}}})(e)),(e=>{let{componentCls:t,inlineDotSize:i,inlineTitleColor:r,inlineTailColor:o}=e,a=e.calc(e.paddingXS).add(e.lineWidth).equal(),n={[`${t}-item-container ${t}-item-content ${t}-item-title`]:{color:r}};return{[`&${t}-inline`]:{width:"auto",display:"inline-flex",[`${t}-item`]:{flex:"none","&-container":{padding:`${(0,A.unit)(a)} ${(0,A.unit)(e.paddingXXS)} 0`,margin:`0 ${(0,A.unit)(e.calc(e.marginXXS).div(2).equal())}`,borderRadius:e.borderRadiusSM,cursor:"pointer",transition:`background-color ${e.motionDurationMid}`,"&:hover":{background:e.controlItemBgHover},"&[role='button']:hover":{opacity:1}},"&-icon":{width:i,height:i,marginInlineStart:`calc(50% - ${(0,A.unit)(e.calc(i).div(2).equal())})`,[`> ${t}-icon`]:{top:0},[`${t}-icon-dot`]:{borderRadius:e.calc(e.fontSizeSM).div(4).equal(),"&::after":{display:"none"}}},"&-content":{width:"auto",marginTop:e.calc(e.marginXS).sub(e.lineWidth).equal()},"&-title":{color:r,fontSize:e.fontSizeSM,lineHeight:e.lineHeightSM,fontWeight:"normal",marginBottom:e.calc(e.marginXXS).div(2).equal()},"&-description":{display:"none"},"&-tail":{marginInlineStart:0,top:e.calc(i).div(2).add(a).equal(),transform:"translateY(-50%)","&:after":{width:"100%",height:e.lineWidth,borderRadius:0,marginInlineStart:0,background:o}},[`&:first-child ${t}-item-tail`]:{width:"50%",marginInlineStart:"50%"},[`&:last-child ${t}-item-tail`]:{display:"block",width:"50%"},"&-wait":Object.assign({[`${t}-item-icon ${t}-icon ${t}-icon-dot`]:{backgroundColor:e.colorBorderBg,border:`${(0,A.unit)(e.lineWidth)} ${e.lineType} ${o}`}},n),"&-finish":Object.assign({[`${t}-item-tail::after`]:{backgroundColor:o},[`${t}-item-icon ${t}-icon ${t}-icon-dot`]:{backgroundColor:o,border:`${(0,A.unit)(e.lineWidth)} ${e.lineType} ${o}`}},n),"&-error":n,"&-active, &-process":Object.assign({[`${t}-item-icon`]:{width:i,height:i,marginInlineStart:`calc(50% - ${(0,A.unit)(e.calc(i).div(2).equal())})`,top:0}},n),[`&:not(${t}-item-active) > ${t}-item-container[role='button']:hover`]:{[`${t}-item-title`]:{color:r}}}}}})(e))}})((0,w.mergeToken)(e,{processIconColor:r,processTitleColor:o,processDescriptionColor:o,processIconBgColor:a,processIconBorderColor:a,processDotColor:a,processTailColor:d,waitTitleColor:n,waitDescriptionColor:n,waitTailColor:d,waitDotColor:t,finishIconColor:a,finishTitleColor:o,finishDescriptionColor:n,finishTailColor:a,finishDotColor:a,errorIconColor:r,errorTitleColor:s,errorDescriptionColor:s,errorTailColor:d,errorIconBgColor:s,errorIconBorderColor:s,errorDotColor:s,stepsNavActiveColor:a,stepsProgressSize:i,inlineDotSize:6,inlineTitleColor:l,inlineTailColor:c}))},e=>({titleLineHeight:e.controlHeight,customIconSize:e.controlHeight,customIconTop:0,customIconFontSize:e.controlHeightSM,iconSize:e.controlHeight,iconTop:-.5,iconFontSize:e.fontSize,iconSizeSM:e.fontSizeHeading3,dotSize:e.controlHeight/4,dotCurrentSize:e.controlHeightLG/4,navArrowColor:e.colorTextDisabled,navContentMaxWidth:"unset",descriptionMaxWidth:140,waitIconColor:e.wireframe?e.colorTextDisabled:e.colorTextLabel,waitIconBgColor:e.wireframe?e.colorBgContainer:e.colorFillContent,waitIconBorderColor:e.wireframe?e.colorTextDisabled:"transparent",finishIconBgColor:e.wireframe?e.colorBgContainer:e.controlItemBgActive,finishIconBorderColor:e.wireframe?e.colorPrimary:e.controlItemBgActive}));var S=e.i(876556),E=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(i[r[o]]=e[r[o]]);return i};let T=e=>{var a,n;let{percent:l,size:s,className:c,rootClassName:d,direction:m,items:g,responsive:u=!0,current:A=0,children:C,style:I}=e,w=E(e,["percent","size","className","rootClassName","direction","items","responsive","current","children","style"]),{xs:x}=(0,b.default)(u),{getPrefixCls:T,direction:O,className:y,style:N}=(0,h.useComponentConfig)("steps"),L=t.useMemo(()=>u&&x?"vertical":m,[u,x,m]),_=(0,f.default)(s),M=T("steps",e.prefixCls),[R,P,z]=k(M),H="inline"===e.type,j=T("",e.iconPrefix),D=(a=g,n=C,a?a:(0,S.default)(n).map(e=>{if(t.isValidElement(e)){let{props:t}=e;return Object.assign({},t)}return null}).filter(e=>e)),B=H?void 0:l,W=Object.assign(Object.assign({},N),I),q=(0,o.default)(y,{[`${M}-rtl`]:"rtl"===O,[`${M}-with-progress`]:void 0!==B},c,d,P,z),X={finish:t.createElement(i.default,{className:`${M}-finish-icon`}),error:t.createElement(r.default,{className:`${M}-error-icon`})};return R(t.createElement(p,Object.assign({icons:X},w,{style:W,current:A,size:_,items:D,itemRender:H?(e,i)=>e.description?t.createElement($.default,{title:e.description},i):i:void 0,stepIcon:({node:e,status:i})=>"process"===i&&void 0!==B?t.createElement("div",{className:`${M}-progress-icon`},t.createElement(v.default,{type:"circle",percent:B,size:"small"===_?32:40,strokeWidth:4,format:()=>null}),e):e,direction:L,prefixCls:M,iconPrefix:j,className:q})))};T.Step=p.Step,e.s(["Steps",0,T],280898)},86408,e=>{"use strict";var t=e.i(843476),i=e.i(271645),r=e.i(618566),o=e.i(934879);function a(){let e=(0,r.useSearchParams)().get("key"),[a,n]=(0,i.useState)(null);return(0,i.useEffect)(()=>{e&&n(e)},[e]),(0,t.jsx)(o.default,{accessToken:a,publicPage:!0,premiumUser:!1,userRole:null})}e.s(["default",0,function(){return(0,t.jsx)(i.Suspense,{fallback:(0,t.jsx)("div",{className:"flex items-center justify-center min-h-screen",children:"Loading..."}),children:(0,t.jsx)(a,{})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/16-7emebrt-xf.js b/litellm/proxy/_experimental/out/_next/static/chunks/00-xblkiz3o8~.js similarity index 64% rename from litellm/proxy/_experimental/out/_next/static/chunks/16-7emebrt-xf.js rename to litellm/proxy/_experimental/out/_next/static/chunks/00-xblkiz3o8~.js index d3ffb6638d4..53ea96e5a69 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/16-7emebrt-xf.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/00-xblkiz3o8~.js @@ -1 +1 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,372024,e=>{"use strict";var t=e.i(843476),a=e.i(994388),l=e.i(304967),s=e.i(350967),r=e.i(35983),i=e.i(793130),n=e.i(197647),o=e.i(653824),c=e.i(269200),d=e.i(942232),u=e.i(977572),m=e.i(427612),h=e.i(64848),x=e.i(496020),g=e.i(881073),f=e.i(404206),p=e.i(723731),y=e.i(599724),j=e.i(779241),b=e.i(271645),C=e.i(464571),k=e.i(808613),v=e.i(311451),T=e.i(212931),w=e.i(199133),_=e.i(898586),N=e.i(727749),S=e.i(602869),E=e.i(312361),F=e.i(482725),I=e.i(536916);let{Title:P}=_.Typography,A=({accessToken:e})=>{let[s,r]=(0,b.useState)(!0),[i,n]=(0,b.useState)([]);(0,b.useEffect)(()=>{o()},[e]);let o=async()=>{if(e){r(!0);try{let t=await (0,S.getEmailEventSettings)(e);n(t.settings)}catch(e){console.error("Failed to fetch email event settings:",e),N.default.fromBackend(e)}finally{r(!1)}}},c=async()=>{if(e)try{await (0,S.updateEmailEventSettings)(e,{settings:i}),N.default.success("Email event settings updated successfully")}catch(e){console.error("Failed to update email event settings:",e),N.default.fromBackend(e)}},d=async()=>{if(e)try{await (0,S.resetEmailEventSettings)(e),N.default.success("Email event settings reset to defaults"),o()}catch(e){console.error("Failed to reset email event settings:",e),N.default.fromBackend(e)}};return(0,t.jsxs)(l.Card,{children:[(0,t.jsx)(P,{level:4,children:"Email Notifications"}),(0,t.jsx)(y.Text,{children:"Select which events should trigger email notifications."}),(0,t.jsx)(E.Divider,{}),s?(0,t.jsx)("div",{style:{textAlign:"center",padding:"20px"},children:(0,t.jsx)(F.Spin,{size:"large"})}):(0,t.jsx)("div",{className:"space-y-4",children:i.map(e=>(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(I.Checkbox,{checked:e.enabled,onChange:t=>{var a,l;return a=e.event,l=t.target.checked,void n(i.map(e=>e.event===a?{...e,enabled:l}:e))}}),(0,t.jsxs)("div",{className:"ml-3",children:[(0,t.jsx)(y.Text,{children:e.event}),(0,t.jsx)("div",{className:"text-sm text-gray-500 block",children:(e=>{if(e.includes("Virtual Key Created"))return"An email will be sent to the user when a new virtual key is created with their user ID";{if(e.includes("New User Invitation"))return"An email will be sent to the email address of the user when a new user is created";let t=e.split(/(?=[A-Z])/).join(" ").toLowerCase();return`Receive an email notification when ${t}`}})(e.event)})]})]},e.event))}),(0,t.jsxs)("div",{className:"mt-6 flex space-x-4",children:[(0,t.jsx)(a.Button,{onClick:c,disabled:s,children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:d,variant:"secondary",disabled:s,children:"Reset to Defaults"})]})]})},{Title:B}=_.Typography,L=({accessToken:e,premiumUser:r,alerts:i})=>{let n=async()=>{if(!e)return;let t={};i.filter(e=>"email"===e.name).forEach(e=>{Object.entries(e.variables??{}).forEach(([e,a])=>{let l=document.querySelector(`input[name="${e}"]`);l&&l.value&&(t[e]=l?.value)})});try{await (0,S.setCallbacksCall)(e,{general_settings:{alerting:["email"]},environment_variables:t}),N.default.success("Email settings updated successfully")}catch(e){N.default.fromBackend(e)}};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"mt-6 mb-6",children:(0,t.jsx)(A,{accessToken:e})}),(0,t.jsxs)(l.Card,{children:[(0,t.jsx)(B,{level:4,children:"Email Server Settings"}),(0,t.jsxs)(y.Text,{children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:[" ","LiteLLM Docs: email alerts"]})," ",(0,t.jsx)("br",{})]}),(0,t.jsx)("div",{className:"flex w-full",children:i.filter(e=>"email"===e.name).map((e,a)=>(0,t.jsx)(u.TableCell,{children:(0,t.jsx)("ul",{children:(0,t.jsx)(s.Grid,{numItems:2,children:Object.entries(e.variables??{}).map(([e,a])=>(0,t.jsxs)("li",{className:"mx-2 my-2",children:[!0!=r&&("EMAIL_LOGO_URL"===e||"EMAIL_SUPPORT_CONTACT"===e)?(0,t.jsxs)("div",{children:[(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,t.jsxs)(y.Text,{className:"mt-2",children:[" ✨ ",e]})}),(0,t.jsx)(j.TextInput,{name:e,defaultValue:a,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,t.jsxs)("div",{children:[(0,t.jsx)(y.Text,{className:"mt-2",children:e}),(0,t.jsx)(j.TextInput,{name:e,defaultValue:a,type:"password",style:{width:"400px"}})]}),(0,t.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===e&&(0,t.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===e&&(0,t.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===e&&(0,t.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},e))})})},a))}),(0,t.jsx)(a.Button,{className:"mt-2",onClick:()=>n(),children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:async()=>{if(e)try{await (0,S.serviceHealthCheck)(e,"email"),N.default.success("Email test triggered. Check your configured email inbox/logs.")}catch(e){N.default.fromBackend(e)}},className:"mx-2",children:"Test Email Alerts"})]})]})};var z=e.i(555987),O=e.i(905536),D=e.i(28651),U=e.i(68155),Z=e.i(220508),R=e.i(389083),M=e.i(752978);let q=({alertingSettings:e,handleInputChange:l,handleResetField:s,handleSubmit:r,premiumUser:n})=>{let[o]=k.Form.useForm();return(0,t.jsxs)(k.Form,{form:o,onFinish:()=>{let e=o.getFieldsValue();Object.entries(e).every(([e,t])=>"boolean"!=typeof t&&(""===t||null==t))||r(e)},labelAlign:"left",children:[e.map((e,r)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsxs)(u.TableCell,{align:"center",children:[(0,t.jsx)(y.Text,{children:e.field_name}),(0,t.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?n?(0,t.jsx)(k.Form.Item,{name:e.field_name,children:(0,t.jsx)(u.TableCell,{children:"Integer"===e.field_type?(0,t.jsx)(D.InputNumber,{step:1,value:e.field_value,onChange:t=>l(e.field_name,t)}):"Boolean"===e.field_type?(0,t.jsx)(i.Switch,{checked:e.field_value,onChange:t=>l(e.field_name,t)}):(0,t.jsx)(v.Input,{value:e.field_value,onChange:t=>l(e.field_name,t)})})}):(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(a.Button,{className:"flex items-center justify-center",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,t.jsx)(k.Form.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,t.jsx)(u.TableCell,{children:"Integer"===e.field_type?(0,t.jsx)(D.InputNumber,{step:1,value:e.field_value,onChange:t=>l(e.field_name,t),className:"p-0"}):"Boolean"===e.field_type?(0,t.jsx)(i.Switch,{checked:e.field_value,onChange:t=>{l(e.field_name,t),o.setFieldsValue({[e.field_name]:t})}}):(0,t.jsx)(v.Input,{value:e.field_value,onChange:t=>l(e.field_name,t)})})}),(0,t.jsx)(u.TableCell,{children:!0==e.stored_in_db?(0,t.jsx)(R.Badge,{icon:Z.CheckCircleIcon,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,t.jsx)(R.Badge,{className:"text-gray bg-white outline-solid",children:"In Config"}):(0,t.jsx)(R.Badge,{className:"text-gray bg-white outline-solid",children:"Not Set"})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(M.Icon,{icon:U.TrashIcon,color:"red",onClick:()=>s(e.field_name,r),children:"Reset"})})]},r)),(0,t.jsx)("div",{children:(0,t.jsx)(C.Button,{htmlType:"submit",children:"Update Settings"})})]})},$=({accessToken:e,premiumUser:a})=>{let[l,s]=(0,b.useState)([]);return(0,b.useEffect)(()=>{e&&(0,S.alertingSettingsCall)(e).then(e=>{s(e)})},[e]),(0,t.jsx)(q,{alertingSettings:l,handleInputChange:(e,t)=>{s(l.map(a=>a.field_name===e?{...a,field_value:t}:a))},handleResetField:(t,a)=>{if(e)try{let e=l.map(e=>e.field_name===t?{...e,stored_in_db:null,field_value:e.field_default_value}:e);s(e)}catch(e){}},handleSubmit:t=>{if(!e||null==t||void 0==t)return;let a={};l.forEach(e=>{a[e.field_name]=e.field_value});let{slack_alerting:s,...r}={...t,...a};try{(0,S.updateConfigFieldSetting)(e,"alerting_args",r),"boolean"==typeof s&&(!0==s?(0,S.updateConfigFieldSetting)(e,"alerting",["slack"]):(0,S.updateConfigFieldSetting)(e,"alerting",[])),N.default.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:a})};var H=e.i(954616),G=e.i(266027),K=e.i(912598),W=e.i(243652);let Q=(0,W.createQueryKeys)("cloudZeroSettings"),V=async e=>{let t=(0,S.getProxyBaseUrl)(),a=t?`${t}/cloudzero/settings`:"/cloudzero/settings",l=await fetch(a,{method:"GET",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e="Failed to fetch CloudZero settings";try{let t=await l.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=l.statusText||e}throw Error(e)}let s=await l.json();return s&&(s.api_key_masked||s.connection_id)?s:null},J=async(e,t)=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/settings`:"/cloudzero/settings",s=await fetch(l,{method:"PUT",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({...t.connection_id&&{connection_id:t.connection_id},...t.timezone&&{timezone:t.timezone},...t.api_key&&{api_key:t.api_key}})});if(!s.ok){let e="Failed to update CloudZero settings";try{let t=await s.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=s.statusText||e}throw Error(e)}return await s.json()},X=async e=>{let t=(0,S.getProxyBaseUrl)(),a=t?`${t}/cloudzero/delete`:"/cloudzero/delete",l=await fetch(a,{method:"DELETE",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e="Failed to delete CloudZero settings";try{let t=await l.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=l.statusText||e}throw Error(e)}return await l.json()};var Y=e.i(135214),ee=e.i(175712),et=e.i(21548);let{Title:ea,Paragraph:el}=_.Typography;function es({startCreation:e}){return(0,t.jsx)("div",{className:"bg-white p-12 rounded-lg border border-dashed border-gray-300 text-center max-w-2xl mx-auto mt-8",children:(0,t.jsx)(et.Empty,{image:et.Empty.PRESENTED_IMAGE_SIMPLE,description:(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(ea,{level:4,children:"No CloudZero Integration Found"}),(0,t.jsx)(el,{type:"secondary",className:"max-w-md mx-auto",children:"Connect your CloudZero account to start tracking and analyzing your cloud costs directly from LiteLLM."})]}),children:(0,t.jsx)(C.Button,{type:"primary",size:"large",onClick:e,className:"flex items-center gap-2 mx-auto mt-4",children:"Add CloudZero Integration"})})})}var er=e.i(888259);let ei=async(e,t)=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/init`:"/cloudzero/init",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({connection_id:t.connection_id,timezone:t.timezone??"UTC",...t.api_key&&{api_key:t.api_key}})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to create CloudZero integration")}return await s.json()};function en({open:e,onOk:a,onCancel:l}){let s,{accessToken:r}=(0,Y.default)(),[i]=k.Form.useForm(),n=(s=r||"",(0,H.useMutation)({mutationFn:async e=>{if(!s)throw Error("Access token is required");return await ei(s,e)}}));(0,b.useEffect)(()=>{e&&i.resetFields()},[e,i]);let o=async()=>{try{let e=await i.validateFields();n.mutate({connection_id:e.connection_id,timezone:e.timezone||"UTC",...e.api_key&&{api_key:e.api_key}},{onSuccess:()=>{er.default.success("CloudZero integration created successfully"),i.resetFields(),a()},onError:e=>{e?.errorFields||er.default.error(e?.message||"Failed to create CloudZero integration")}})}catch(e){if(e?.errorFields)return;er.default.error(e?.message||"Failed to create CloudZero integration")}};return(0,t.jsx)(T.Modal,{title:"Create CloudZero Integration",open:e,onOk:o,onCancel:()=>{i.resetFields(),l()},confirmLoading:n.isPending,okText:n.isPending?"Creating...":"Create",cancelText:"Cancel",okButtonProps:{disabled:n.isPending},cancelButtonProps:{disabled:n.isPending},children:(0,t.jsxs)(k.Form,{form:i,layout:"vertical",onFinish:o,children:[(0,t.jsx)(k.Form.Item,{label:"CloudZero API Key",name:"api_key",rules:[{required:!0,message:"Please enter your CloudZero API key"}],children:(0,t.jsx)(v.Input.Password,{placeholder:"Enter your CloudZero API key"})}),(0,t.jsx)(k.Form.Item,{label:"Connection ID",name:"connection_id",rules:[{required:!0,message:"Please enter your CloudZero connection ID"}],children:(0,t.jsx)(v.Input,{placeholder:"Enter your CloudZero connection ID"})}),(0,t.jsx)(k.Form.Item,{label:"Timezone",name:"timezone",tooltip:"Timezone for date handling (defaults to UTC if not provided)",children:(0,t.jsx)(v.Input,{placeholder:"UTC"})})]})})}let eo=async(e,t={})=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/dry-run`:"/cloudzero/dry-run",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({limit:t.limit??10})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to perform dry run")}return await s.json()},ec=async(e,t={})=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/export`:"/cloudzero/export",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({operation:t.operation??"replace_hourly"})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to export data")}return await s.json()};var ed=e.i(127952),eu=e.i(560445),em=e.i(869216),eh=e.i(883552),ex=e.i(262218),eg=e.i(269638),ef=e.i(688511),ep=e.i(431343),ey=e.i(727612),ej=e.i(569074);function eb({open:e,onOk:a,onCancel:l,settings:s}){var r;let i,{accessToken:n}=(0,Y.default)(),[o]=k.Form.useForm(),c=(r=n||"",i=(0,K.useQueryClient)(),(0,H.useMutation)({mutationFn:async e=>{if(!r)throw Error("Access token is required");return await J(r,e)},onSuccess:()=>{i.invalidateQueries({queryKey:Q.list({})})}}));(0,b.useEffect)(()=>{e&&s?o.setFieldsValue({connection_id:s.connection_id,timezone:s.timezone||"UTC",api_key:""}):e&&o.resetFields()},[e,s,o]);let d=async()=>{try{let e=await o.validateFields();c.mutate({connection_id:e.connection_id,timezone:e.timezone||"UTC",...e.api_key&&{api_key:e.api_key}},{onSuccess:()=>{er.default.success("CloudZero integration updated successfully"),o.resetFields(),a()},onError:e=>{e?.errorFields||er.default.error(e?.message||"Failed to update CloudZero integration")}})}catch(e){if(e?.errorFields)return;er.default.error(e?.message||"Failed to update CloudZero integration")}};return(0,t.jsx)(T.Modal,{title:"Edit CloudZero Integration",open:e,onOk:d,onCancel:()=>{o.resetFields(),l()},confirmLoading:c.isPending,okText:c.isPending?"Updating...":"Update",cancelText:"Cancel",okButtonProps:{disabled:c.isPending},cancelButtonProps:{disabled:c.isPending},children:(0,t.jsxs)(k.Form,{form:o,layout:"vertical",onFinish:d,children:[(0,t.jsx)(k.Form.Item,{label:"CloudZero API Key",name:"api_key",rules:[{required:!1,message:"Please enter your CloudZero API key"}],tooltip:"Leave empty to keep the existing API key",children:(0,t.jsx)(v.Input.Password,{placeholder:"Leave empty to keep existing"})}),(0,t.jsx)(k.Form.Item,{label:"Connection ID",name:"connection_id",rules:[{required:!0,message:"Please enter your CloudZero connection ID"}],children:(0,t.jsx)(v.Input,{placeholder:"Enter your CloudZero connection ID"})}),(0,t.jsx)(k.Form.Item,{label:"Timezone",name:"timezone",tooltip:"Timezone for date handling (defaults to UTC if not provided)",children:(0,t.jsx)(v.Input,{placeholder:"UTC"})})]})})}function eC({settings:e,onSettingsUpdated:a}){var l;let s,r,i,{accessToken:n}=(0,Y.default)(),[o,c]=(0,b.useState)(!1),[d,u]=(0,b.useState)(!1),m=(s=n||"",(0,H.useMutation)({mutationFn:async(e={})=>{if(!s)throw Error("Access token is required");return await eo(s,e)}})),h=(r=n||"",(0,H.useMutation)({mutationFn:async(e={})=>{if(!r)throw Error("Access token is required");return await ec(r,e)}})),x=(l=n||"",i=(0,K.useQueryClient)(),(0,H.useMutation)({mutationFn:async()=>{if(!l)throw Error("Access token is required");return await X(l)},onSuccess:()=>{i.invalidateQueries({queryKey:Q.list({})})}})),g=m.data?JSON.stringify(m.data,null,2):null,f=async()=>{c(!1),a()};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"space-y-6 w-full max-w-4xl mx-auto",children:(0,t.jsxs)(ee.Card,{title:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"text-lg font-semibold",children:"CloudZero Configuration"}),(0,t.jsx)(ex.Tag,{color:"success",className:"ml-2 capitalize",children:e.status||"Active"})]}),extra:(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(C.Button,{icon:(0,t.jsx)(ef.Edit,{size:16}),onClick:()=>{c(!0)},className:"flex items-center gap-2",children:"Edit"}),(0,t.jsx)(C.Button,{danger:!0,icon:(0,t.jsx)(ey.Trash2,{size:16}),onClick:()=>{u(!0)},className:"flex items-center gap-2",children:"Delete"})]}),className:"shadow-xs",children:[(0,t.jsxs)(em.Descriptions,{bordered:!0,column:{xxl:1,xl:1,lg:1,md:1,sm:1,xs:1},children:[(0,t.jsx)(em.Descriptions.Item,{label:"API Key (Redacted)",children:(0,t.jsx)("span",{className:"font-mono text-gray-600",children:e.api_key_masked||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Not configured"})})}),(0,t.jsx)(em.Descriptions.Item,{label:"Connection ID",children:(0,t.jsx)("span",{className:"font-mono text-gray-600",children:e.connection_id||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Not configured"})})}),(0,t.jsx)(em.Descriptions.Item,{label:"Timezone",children:e.timezone||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Default (UTC)"})})]}),(0,t.jsx)(E.Divider,{orientation:"left",className:"text-gray-500",children:"Actions"}),(0,t.jsxs)("div",{className:"flex flex-wrap gap-4 mb-6",children:[(0,t.jsx)(C.Button,{onClick:()=>{n&&m.mutate({limit:10},{onSuccess:e=>{er.default.success("Dry run completed successfully")},onError:e=>{er.default.error(e?.message||"Failed to perform dry run")}})},loading:m.isPending,icon:(0,t.jsx)(ep.Play,{size:16}),className:"flex items-center gap-2",children:"Run Dry Run Simulation"}),(0,t.jsx)(eh.Popconfirm,{title:"Export Data to CloudZero",description:"This will push the current accumulated cost data to CloudZero. Continue?",onConfirm:()=>{n&&h.mutate({operation:"replace_hourly"},{onSuccess:()=>{er.default.success("Data successfully exported to CloudZero")},onError:e=>{er.default.error(e?.message||"Failed to export data")}})},okText:"Export",cancelText:"Cancel",children:(0,t.jsx)(C.Button,{type:"primary",loading:h.isPending,icon:(0,t.jsx)(ej.Upload,{size:16}),className:"flex items-center gap-2",children:"Export Data Now"})})]}),g&&(0,t.jsx)("div",{className:"mt-6 animate-in fade-in slide-in-from-top-4 duration-300",children:(0,t.jsx)(eu.Alert,{message:"Dry Run Results",description:(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)("p",{className:"mb-2 text-gray-600",children:["Simulation output for connection: ",e.connection_id]}),(0,t.jsx)("pre",{className:"bg-gray-50 p-4 rounded-md border border-gray-200 overflow-x-auto text-xs font-mono text-gray-800",children:g})]}),type:"info",showIcon:!0,icon:(0,t.jsx)(eg.CheckCircle,{className:"text-blue-500"})})})]})}),(0,t.jsx)(eb,{open:o,onOk:f,onCancel:()=>{c(!1)},settings:e}),(0,t.jsx)(ed.default,{isOpen:d,title:"Delete CloudZero Integration?",message:"Are you sure you want to delete this CloudZero integration? All associated settings and configurations will be permanently removed.",resourceInformationTitle:"Integration Details",resourceInformation:[{label:"Connection ID",value:e.connection_id,code:!0},{label:"Timezone",value:e.timezone||"Default (UTC)"}],onCancel:()=>{u(!1)},onOk:()=>{n&&x.mutate(void 0,{onSuccess:()=>{er.default.success("CloudZero integration deleted successfully"),u(!1),a()},onError:e=>{er.default.error(e?.message||"Failed to delete CloudZero integration")}})},confirmLoading:x.isPending})]})}function ek(){let{accessToken:e}=(0,Y.default)(),{data:a,isLoading:l,error:s}=(0,G.useQuery)({queryKey:Q.list({}),queryFn:async()=>await V(e),enabled:!!e,staleTime:36e5,gcTime:36e5}),r=(0,K.useQueryClient)(),i=(0,W.createQueryKeys)("cloudZeroSettings"),[n,o]=(0,b.useState)(!1),c=async()=>{o(!1),await r.invalidateQueries({queryKey:i.list({})})};return l?(0,t.jsx)(ee.Card,{children:(0,t.jsx)(_.Typography.Text,{children:"Loading CloudZero settings..."})}):s?(0,t.jsx)(ee.Card,{children:(0,t.jsxs)(_.Typography.Text,{className:"text-red-600",children:["Error loading CloudZero settings: ",s instanceof Error?s.message:String(s)]})}):a?(0,t.jsx)(t.Fragment,{children:(0,t.jsx)(eC,{settings:a,onSettingsUpdated:c})}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(es,{startCreation:()=>o(!0)}),(0,t.jsx)(en,{open:n,onOk:c,onCancel:()=>{o(!1)}})]})}var ev=e.i(291542),eT=e.i(335771),ew=e.i(902555);let e_=[{value:"success",label:"Success"},{value:"failure",label:"Failure"},{value:"success_and_failure",label:"Success & Failure"}],eN=({callbacks:e,availableCallbacks:l={},onTest:s=()=>{},onEdit:r=()=>{},onDelete:i=()=>{},onAdd:n=()=>{}})=>{let o=[{title:(0,t.jsx)("span",{className:"font-medium text-gray-700",children:"Callback Name"}),dataIndex:"name",key:"name",render:(e,a)=>{let s=a.name,r=l[s]?.ui_callback_name||s;return(0,t.jsx)("div",{className:"font-medium text-gray-800",children:r})}},{title:(0,t.jsx)("span",{className:"font-medium text-gray-700",children:"Mode"}),key:"mode",render:(e,a)=>{let l=a.type||a.mode||"success",s=e_.find(e=>e.value===l)?.label||l,r="success"===l?"bg-green-100 text-green-800":"failure"===l?"bg-red-100 text-red-800":"bg-blue-100 text-blue-800";return(0,t.jsx)("span",{className:`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${r}`,children:s})},width:240},{title:(0,t.jsx)("span",{className:"font-medium text-gray-700 text-right w-full block",children:"Actions"}),key:"actions",align:"right",render:(e,a)=>(0,t.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,t.jsx)(ew.default,{variant:"Test",tooltipText:"Test Callback",onClick:()=>s(a)}),(0,t.jsx)(ew.default,{variant:"Edit",tooltipText:"Edit Callback",onClick:()=>r(a)}),(0,t.jsx)(ew.default,{variant:"Delete",tooltipText:"Delete Callback",onClick:()=>i(a)})]}),width:240}];return(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{className:"w-full mt-4",children:[(0,t.jsx)(a.Button,{onClick:n,className:"mx-auto",children:"+ Add Callback"}),(0,t.jsx)("div",{className:"flex justify-between items-center my-2",children:(0,t.jsx)(eT.default,{level:4,children:"Active Logging Callbacks"})}),0===e.length?(0,t.jsx)("div",{className:"flex flex-col items-center justify-center p-8 bg-gray-50 border border-gray-200 rounded-lg",children:(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-700 mb-2",children:"No callbacks configured"}),(0,t.jsx)("p",{className:"text-gray-500",children:"Add your first callback to start logging data to external services."})]})}):(0,t.jsx)("div",{className:"bg-white border border-gray-200 rounded-lg overflow-hidden",children:(0,t.jsx)(ev.Table,{columns:o,dataSource:e,rowKey:e=>`${e.name}-${e.type||e.mode||"success"}`,pagination:!1,rowClassName:()=>"hover:bg-gray-50"})})]})})};var eS=e.i(190702);let{Title:eE,Paragraph:eF}=_.Typography,eI=({params:e,callbackConfigs:a,selectedCallback:l})=>e&&0!==e.length?(0,t.jsx)("div",{className:"space-y-4 mt-6 p-4 bg-gray-50 rounded-lg border",children:e.map(e=>{let s=a.find(e=>e.id===l),r=s?.dynamic_params?.[e]||{},i=r.type||"text",n=r.ui_name||e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),o=r.required||!1;return(0,t.jsx)(O.default,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:[n," "]}),name:e,className:"mb-4",rules:o?[{required:!0,message:`Please enter the ${n.toLowerCase()}`}]:void 0,children:"password"===i?(0,t.jsx)(v.Input.Password,{size:"large",placeholder:`Enter your ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500"}):"number"===i?(0,t.jsx)(v.Input,{type:"number",size:"large",placeholder:`Enter ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500",min:0,max:1,step:.1}):(0,t.jsx)(v.Input,{size:"large",placeholder:`Enter your ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500"})},e)})}):null,eP=({callbackConfigs:e,selectedCallback:a,onCallbackChange:l,disabled:s=!1})=>(0,t.jsx)(O.default,{label:"Callback",name:"callback",rules:s?void 0:[{required:!0,message:"Please select a callback"}],children:(0,t.jsx)(w.Select,{placeholder:"Choose a logging callback...",size:"large",className:"w-full",showSearch:!0,disabled:s,value:a,filterOption:(e,t)=>(t?.value?.toString()??"").toLowerCase().includes(e.toLowerCase()),onChange:l,children:e.map(e=>{let a=e.logo,l=(0,z.resolveLogoSrc)(a&&(a.includes("/")||a.startsWith("data:")||a.startsWith("http"))?a:`/ui/assets/logos/${a}`);return(0,t.jsx)(r.SelectItem,{value:e.id,children:(0,t.jsxs)("div",{className:"flex items-center space-x-3 py-1",children:[(0,t.jsx)("div",{className:"w-6 h-6 flex items-center justify-center",children:(0,t.jsx)("img",{src:l,alt:`${e.displayName} logo`,className:"w-6 h-6 rounded-sm object-contain",onError:e=>{e.currentTarget.style.display="none"}})}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:e.displayName})]})},e.id)})})}),eA=(e,t,a)=>{if(!e)return a?Object.keys(a):[];let l=t.find(t=>t.id===e);return l?.dynamic_params?Object.keys(l.dynamic_params):a?Object.keys(a):[]},eB=({accessToken:e,userRole:r,userID:v,premiumUser:w})=>{let[_,E]=(0,b.useState)([]),[F,I]=(0,b.useState)([]),[P,A]=(0,b.useState)(!1),[B]=k.Form.useForm(),[z]=k.Form.useForm(),[O,D]=(0,b.useState)(null),[U,Z]=(0,b.useState)(""),[R,M]=(0,b.useState)({}),[q,H]=(0,b.useState)([]),[G,K]=(0,b.useState)(!1),[W,Q]=(0,b.useState)([]),[V,J]=(0,b.useState)({}),[X,Y]=(0,b.useState)([]),[ee,et]=(0,b.useState)(!1),[ea,el]=(0,b.useState)(null),[es,er]=(0,b.useState)(!1),[ei,en]=(0,b.useState)(null),[eo,ec]=(0,b.useState)(!1),[eu,em]=(0,b.useState)(!1),[eh,ex]=(0,b.useState)(!1);(0,b.useEffect)(()=>{e&&(0,S.getCallbackConfigsCall)(e).then(e=>{Q(e||[])}).catch(e=>{N.default.fromBackend("Failed to load callback configs: "+(0,eS.parseErrorMessage)(e))})},[e]),(0,b.useEffect)(()=>{if(ee&&ea){let e=Object.fromEntries(Object.entries(ea.variables||{}).map(([e,t])=>[e,t??""]));z.setFieldsValue({...e,callback:ea.name})}},[ee,ea,z]);let eg=e=>{q.includes(e)?H(q.filter(t=>t!==e)):H([...q,e])},ef={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,b.useEffect)(()=>{e&&r&&v&&(0,S.getCallbacksCall)(e,v,r).then(e=>{E(e.callbacks),J(e.available_callbacks);let t=e.alerts;if(t&&t.length>0){let e=t[0],a=e.variables.SLACK_WEBHOOK_URL;H(e.active_alerts),Z(a),M(e.alerts_to_webhook)}I(t)})},[e,r,v]);let ep=e=>q&&q.includes(e),ey=async(t,a,l)=>{if(e){l?ec(!0):em(!0);try{if(await (0,S.setCallbacksCall)(e,{environment_variables:t,litellm_settings:{success_callback:[a]}}),N.default.success(l?"Callback updated successfully":`Callback ${a} added successfully`),l?(et(!1),z.resetFields(),el(null)):(K(!1),B.resetFields(),D(null),Y([])),v&&r){let t=await (0,S.getCallbacksCall)(e,v,r);E(t.callbacks)}}catch(e){N.default.fromBackend(e)}finally{l?ec(!1):em(!1)}}},ej=async e=>{ea&&await ey(e,ea.name,!0)},eb=async e=>{let t=e?.callback;t&&await ey(e,t,!1)},eC=async()=>{if(!e)return;let t={};Object.entries(ef).forEach(([e,a])=>{let l=document.querySelector(`input[name="${e}"]`),s=l?.value||"";t[e]=s});try{await (0,S.setCallbacksCall)(e,{general_settings:{alert_to_webhook_url:t,alert_types:q}})}catch(e){N.default.fromBackend(e)}N.default.success("Alerts updated successfully")},ev=async()=>{if(ei&&e)try{if(ex(!0),await (0,S.deleteCallback)(e,ei.name),N.default.success(`Callback ${ei.name} deleted successfully`),v&&r){let t=await (0,S.getCallbacksCall)(e,v,r);E(t.callbacks)}er(!1),en(null)}catch(e){console.error("Failed to delete callback:",e),N.default.fromBackend(e)}finally{ex(!1)}};return e?(0,t.jsxs)("div",{className:"w-full mx-4",children:[(0,t.jsx)(s.Grid,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,t.jsxs)(o.TabGroup,{children:[(0,t.jsxs)(g.TabList,{variant:"line",defaultValue:"1",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Logging Callbacks"}),(0,t.jsx)(n.Tab,{value:"2",children:"CloudZero Cost Tracking"}),(0,t.jsx)(n.Tab,{value:"2",children:"Alerting Types"}),(0,t.jsx)(n.Tab,{value:"3",children:"Alerting Settings"}),(0,t.jsx)(n.Tab,{value:"4",children:"Email Alerts"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)(eN,{callbacks:_,availableCallbacks:V,onAdd:()=>K(!0),onEdit:e=>{el(e),et(!0)},onDelete:e=>{en(e),er(!0)},onTest:async t=>{try{await (0,S.serviceHealthCheck)(e,t.name),N.default.success("Health check triggered")}catch(e){N.default.fromBackend((0,eS.parseErrorMessage)(e))}}})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)("div",{className:"p-8",children:(0,t.jsx)(ek,{})})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsxs)(l.Card,{children:[(0,t.jsxs)(y.Text,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,t.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,t.jsxs)(c.Table,{children:[(0,t.jsx)(m.TableHead,{children:(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(h.TableHeaderCell,{}),(0,t.jsx)(h.TableHeaderCell,{}),(0,t.jsx)(h.TableHeaderCell,{children:"Slack Webhook URL"})]})}),(0,t.jsx)(d.TableBody,{children:Object.entries(ef).map(([e,l],s)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(u.TableCell,{children:"region_outage_alerts"==e?w?(0,t.jsx)(i.Switch,{id:"switch",name:"switch",checked:ep(e),onChange:()=>eg(e)}):(0,t.jsx)(a.Button,{className:"flex items-center justify-center",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,t.jsx)(i.Switch,{id:"switch",name:"switch",checked:ep(e),onChange:()=>eg(e)})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(y.Text,{children:l})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(j.TextInput,{name:e,type:"password",defaultValue:R&&R[e]?R[e]:U})})]},s))})]}),(0,t.jsx)(a.Button,{size:"xs",className:"mt-2",onClick:eC,children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:async()=>{try{await (0,S.serviceHealthCheck)(e,"slack"),N.default.success("Alert test triggered. Test request to slack made - check logs/alerts on slack to verify")}catch(e){N.default.fromBackend((0,eS.parseErrorMessage)(e))}},className:"mx-2",children:"Test Alerts"})]})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)($,{accessToken:e,premiumUser:w})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)(L,{accessToken:e,premiumUser:w,alerts:F})})]})]})}),(0,t.jsxs)(T.Modal,{title:"Add Logging Callback",open:G,width:800,onCancel:()=>{K(!1),D(null),Y([])},footer:null,children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:[" ","LiteLLM Docs: Logging"]}),(0,t.jsxs)(k.Form,{form:B,onFinish:eb,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsx)(eP,{callbackConfigs:W,selectedCallback:O,onCallbackChange:e=>{D(e),Y(eA(e,W))}}),(0,t.jsx)(eI,{params:X,callbackConfigs:W,selectedCallback:O}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-6 mt-6 border-t border-gray-200",children:[(0,t.jsx)(C.Button,{onClick:()=>{K(!1),D(null),Y([]),B.resetFields()},disabled:eu,children:"Cancel"}),(0,t.jsx)(C.Button,{htmlType:"submit",loading:eu,disabled:eu,children:eu?"Adding...":"Add Callback"})]})]})]}),(0,t.jsx)(T.Modal,{open:ee,width:800,title:"Edit Callback Settings",onCancel:()=>{et(!1),el(null),z.resetFields()},footer:null,children:(0,t.jsxs)(k.Form,{form:z,onFinish:ej,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[ea&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eP,{callbackConfigs:W,selectedCallback:ea.name,onCallbackChange:()=>{},disabled:!0}),(0,t.jsx)(eI,{params:eA(ea.name,W,ea.variables),callbackConfigs:W,selectedCallback:ea.name})]}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-6 mt-6 border-t border-gray-200",children:[(0,t.jsx)(C.Button,{onClick:()=>{et(!1),el(null),z.resetFields()},disabled:eo,children:"Cancel"}),(0,t.jsx)(C.Button,{onClick:()=>{z.submit()},loading:eo,disabled:eo,children:eo?"Saving...":"Save Changes"})]})]})}),(0,t.jsx)(ed.default,{isOpen:es,title:"Delete Callback",message:"Are you sure you want to delete this callback? This action cannot be undone.",resourceInformationTitle:"Callback Information",resourceInformation:[{label:"Callback Name",value:ei?.name},{label:"Mode",value:ei?.mode||"success"}],onCancel:()=>{er(!1),en(null)},onOk:ev,confirmLoading:eh})]}):null};e.s(["default",0,function(){let{accessToken:e,userRole:a,userId:l,premiumUser:s}=(0,Y.default)();return(0,t.jsx)(eB,{userID:l,userRole:a,accessToken:e,premiumUser:s})}],372024)}]); \ No newline at end of file +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,372024,e=>{"use strict";var t=e.i(843476),a=e.i(994388),l=e.i(304967),s=e.i(350967),r=e.i(35983),i=e.i(793130),n=e.i(197647),o=e.i(653824),c=e.i(269200),d=e.i(942232),u=e.i(977572),m=e.i(427612),h=e.i(64848),x=e.i(496020),g=e.i(881073),f=e.i(404206),p=e.i(723731),y=e.i(599724),j=e.i(779241),b=e.i(271645),C=e.i(464571),k=e.i(808613),v=e.i(311451),T=e.i(212931),w=e.i(199133),_=e.i(898586),N=e.i(727749),S=e.i(602869),E=e.i(312361),F=e.i(482725),I=e.i(536916);let{Title:P}=_.Typography,A=({accessToken:e})=>{let[s,r]=(0,b.useState)(!0),[i,n]=(0,b.useState)([]);(0,b.useEffect)(()=>{o()},[e]);let o=async()=>{if(e){r(!0);try{let t=await (0,S.getEmailEventSettings)(e);n(t.settings)}catch(e){console.error("Failed to fetch email event settings:",e),N.default.fromBackend(e)}finally{r(!1)}}},c=async()=>{if(e)try{await (0,S.updateEmailEventSettings)(e,{settings:i}),N.default.success("Email event settings updated successfully")}catch(e){console.error("Failed to update email event settings:",e),N.default.fromBackend(e)}},d=async()=>{if(e)try{await (0,S.resetEmailEventSettings)(e),N.default.success("Email event settings reset to defaults"),o()}catch(e){console.error("Failed to reset email event settings:",e),N.default.fromBackend(e)}};return(0,t.jsxs)(l.Card,{children:[(0,t.jsx)(P,{level:4,children:"Email Notifications"}),(0,t.jsx)(y.Text,{children:"Select which events should trigger email notifications."}),(0,t.jsx)(E.Divider,{}),s?(0,t.jsx)("div",{style:{textAlign:"center",padding:"20px"},children:(0,t.jsx)(F.Spin,{size:"large"})}):(0,t.jsx)("div",{className:"space-y-4",children:i.map(e=>(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(I.Checkbox,{checked:e.enabled,onChange:t=>{var a,l;return a=e.event,l=t.target.checked,void n(i.map(e=>e.event===a?{...e,enabled:l}:e))}}),(0,t.jsxs)("div",{className:"ml-3",children:[(0,t.jsx)(y.Text,{children:e.event}),(0,t.jsx)("div",{className:"text-sm text-gray-500 block",children:(e=>{if(e.includes("Virtual Key Created"))return"An email will be sent to the user when a new virtual key is created with their user ID";{if(e.includes("New User Invitation"))return"An email will be sent to the email address of the user when a new user is created";let t=e.split(/(?=[A-Z])/).join(" ").toLowerCase();return`Receive an email notification when ${t}`}})(e.event)})]})]},e.event))}),(0,t.jsxs)("div",{className:"mt-6 flex space-x-4",children:[(0,t.jsx)(a.Button,{onClick:c,disabled:s,children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:d,variant:"secondary",disabled:s,children:"Reset to Defaults"})]})]})},{Title:B}=_.Typography,L=({accessToken:e,premiumUser:r,alerts:i})=>{let n=async()=>{if(!e)return;let t={};i.filter(e=>"email"===e.name).forEach(e=>{Object.entries(e.variables??{}).forEach(([e,a])=>{let l=document.querySelector(`input[name="${e}"]`);l&&l.value&&(t[e]=l?.value)})});try{await (0,S.setCallbacksCall)(e,{general_settings:{alerting:["email"]},environment_variables:t}),N.default.success("Email settings updated successfully")}catch(e){N.default.fromBackend(e)}};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"mt-6 mb-6",children:(0,t.jsx)(A,{accessToken:e})}),(0,t.jsxs)(l.Card,{children:[(0,t.jsx)(B,{level:4,children:"Email Server Settings"}),(0,t.jsxs)(y.Text,{children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:[" ","LiteLLM Docs: email alerts"]})," ",(0,t.jsx)("br",{})]}),(0,t.jsx)("div",{className:"flex w-full",children:i.filter(e=>"email"===e.name).map((e,a)=>(0,t.jsx)(u.TableCell,{children:(0,t.jsx)("ul",{children:(0,t.jsx)(s.Grid,{numItems:2,children:Object.entries(e.variables??{}).map(([e,a])=>(0,t.jsxs)("li",{className:"mx-2 my-2",children:[!0!=r&&("EMAIL_LOGO_URL"===e||"EMAIL_SUPPORT_CONTACT"===e)?(0,t.jsxs)("div",{children:[(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,t.jsxs)(y.Text,{className:"mt-2",children:[" ✨ ",e]})}),(0,t.jsx)(j.TextInput,{name:e,defaultValue:a,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,t.jsxs)("div",{children:[(0,t.jsx)(y.Text,{className:"mt-2",children:e}),(0,t.jsx)(j.TextInput,{name:e,defaultValue:a,type:"password",style:{width:"400px"}})]}),(0,t.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===e&&(0,t.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===e&&(0,t.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===e&&(0,t.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},e))})})},a))}),(0,t.jsx)(a.Button,{className:"mt-2",onClick:()=>n(),children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:async()=>{if(e)try{await (0,S.serviceHealthCheck)(e,"email"),N.default.success("Email test triggered. Check your configured email inbox/logs.")}catch(e){N.default.fromBackend(e)}},className:"mx-2",children:"Test Email Alerts"})]})]})};var z=e.i(555987),O=e.i(905536),D=e.i(28651),U=e.i(68155),Z=e.i(220508),R=e.i(389083),M=e.i(752978);let q=({alertingSettings:e,handleInputChange:l,handleResetField:s,handleSubmit:r,premiumUser:n})=>{let[o]=k.Form.useForm();return(0,t.jsxs)(k.Form,{form:o,onFinish:()=>{let e=o.getFieldsValue();Object.entries(e).every(([e,t])=>"boolean"!=typeof t&&(""===t||null==t))||r(e)},labelAlign:"left",children:[e.map((e,r)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsxs)(u.TableCell,{align:"center",children:[(0,t.jsx)(y.Text,{children:e.field_name}),(0,t.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?n?(0,t.jsx)(k.Form.Item,{name:e.field_name,children:(0,t.jsx)(u.TableCell,{children:"Integer"===e.field_type?(0,t.jsx)(D.InputNumber,{step:1,value:e.field_value,onChange:t=>l(e.field_name,t)}):"Boolean"===e.field_type?(0,t.jsx)(i.Switch,{checked:e.field_value,onChange:t=>l(e.field_name,t)}):(0,t.jsx)(v.Input,{value:e.field_value,onChange:t=>l(e.field_name,t)})})}):(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(a.Button,{className:"flex items-center justify-center",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,t.jsx)(k.Form.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,t.jsx)(u.TableCell,{children:"Integer"===e.field_type?(0,t.jsx)(D.InputNumber,{step:1,value:e.field_value,onChange:t=>l(e.field_name,t),className:"p-0"}):"Boolean"===e.field_type?(0,t.jsx)(i.Switch,{checked:e.field_value,onChange:t=>{l(e.field_name,t),o.setFieldsValue({[e.field_name]:t})}}):(0,t.jsx)(v.Input,{value:e.field_value,onChange:t=>l(e.field_name,t)})})}),(0,t.jsx)(u.TableCell,{children:!0==e.stored_in_db?(0,t.jsx)(R.Badge,{icon:Z.CheckCircleIcon,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,t.jsx)(R.Badge,{className:"text-gray bg-white outline-solid",children:"In Config"}):(0,t.jsx)(R.Badge,{className:"text-gray bg-white outline-solid",children:"Not Set"})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(M.Icon,{icon:U.TrashIcon,color:"red",onClick:()=>s(e.field_name,r),children:"Reset"})})]},r)),(0,t.jsx)("div",{children:(0,t.jsx)(C.Button,{htmlType:"submit",children:"Update Settings"})})]})},$=({accessToken:e,premiumUser:a})=>{let[l,s]=(0,b.useState)([]);return(0,b.useEffect)(()=>{e&&(0,S.alertingSettingsCall)(e).then(e=>{s(e)})},[e]),(0,t.jsx)(q,{alertingSettings:l,handleInputChange:(e,t)=>{s(l.map(a=>a.field_name===e?{...a,field_value:t}:a))},handleResetField:(t,a)=>{if(e)try{let e=l.map(e=>e.field_name===t?{...e,stored_in_db:null,field_value:e.field_default_value}:e);s(e)}catch(e){}},handleSubmit:t=>{if(!e||null==t||void 0==t)return;let a={};l.forEach(e=>{a[e.field_name]=e.field_value});let{slack_alerting:s,...r}={...t,...a};try{(0,S.updateConfigFieldSetting)(e,"alerting_args",r),"boolean"==typeof s&&(!0==s?(0,S.updateConfigFieldSetting)(e,"alerting",["slack"]):(0,S.updateConfigFieldSetting)(e,"alerting",[])),N.default.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:a})};var H=e.i(954616),G=e.i(266027),K=e.i(912598),W=e.i(243652);let Q=(0,W.createQueryKeys)("cloudZeroSettings"),V=async e=>{let t=(0,S.getProxyBaseUrl)(),a=t?`${t}/cloudzero/settings`:"/cloudzero/settings",l=await fetch(a,{method:"GET",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e="Failed to fetch CloudZero settings";try{let t=await l.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=l.statusText||e}throw Error(e)}let s=await l.json();return s&&(s.api_key_masked||s.connection_id)?s:null},J=async(e,t)=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/settings`:"/cloudzero/settings",s=await fetch(l,{method:"PUT",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({...t.connection_id&&{connection_id:t.connection_id},...t.timezone&&{timezone:t.timezone},...t.api_key&&{api_key:t.api_key}})});if(!s.ok){let e="Failed to update CloudZero settings";try{let t=await s.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=s.statusText||e}throw Error(e)}return await s.json()},X=async e=>{let t=(0,S.getProxyBaseUrl)(),a=t?`${t}/cloudzero/delete`:"/cloudzero/delete",l=await fetch(a,{method:"DELETE",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e="Failed to delete CloudZero settings";try{let t=await l.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=l.statusText||e}throw Error(e)}return await l.json()};var Y=e.i(135214),ee=e.i(175712),et=e.i(21548);let{Title:ea,Paragraph:el}=_.Typography;function es({startCreation:e}){return(0,t.jsx)("div",{className:"bg-white p-12 rounded-lg border border-dashed border-gray-300 text-center max-w-2xl mx-auto mt-8",children:(0,t.jsx)(et.Empty,{image:et.Empty.PRESENTED_IMAGE_SIMPLE,description:(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(ea,{level:4,children:"No CloudZero Integration Found"}),(0,t.jsx)(el,{type:"secondary",className:"max-w-md mx-auto",children:"Connect your CloudZero account to start tracking and analyzing your cloud costs directly from LiteLLM."})]}),children:(0,t.jsx)(C.Button,{type:"primary",size:"large",onClick:e,className:"flex items-center gap-2 mx-auto mt-4",children:"Add CloudZero Integration"})})})}var er=e.i(888259);let ei=async(e,t)=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/init`:"/cloudzero/init",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({connection_id:t.connection_id,timezone:t.timezone??"UTC",...t.api_key&&{api_key:t.api_key}})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to create CloudZero integration")}return await s.json()};function en({open:e,onOk:a,onCancel:l}){let s,{accessToken:r}=(0,Y.default)(),[i]=k.Form.useForm(),n=(s=r||"",(0,H.useMutation)({mutationFn:async e=>{if(!s)throw Error("Access token is required");return await ei(s,e)}}));(0,b.useEffect)(()=>{e&&i.resetFields()},[e,i]);let o=async()=>{try{let e=await i.validateFields();n.mutate({connection_id:e.connection_id,timezone:e.timezone||"UTC",...e.api_key&&{api_key:e.api_key}},{onSuccess:()=>{er.default.success("CloudZero integration created successfully"),i.resetFields(),a()},onError:e=>{e?.errorFields||er.default.error(e?.message||"Failed to create CloudZero integration")}})}catch(e){if(e?.errorFields)return;er.default.error(e?.message||"Failed to create CloudZero integration")}};return(0,t.jsx)(T.Modal,{title:"Create CloudZero Integration",open:e,onOk:o,onCancel:()=>{i.resetFields(),l()},confirmLoading:n.isPending,okText:n.isPending?"Creating...":"Create",cancelText:"Cancel",okButtonProps:{disabled:n.isPending},cancelButtonProps:{disabled:n.isPending},children:(0,t.jsxs)(k.Form,{form:i,layout:"vertical",onFinish:o,children:[(0,t.jsx)(k.Form.Item,{label:"CloudZero API Key",name:"api_key",rules:[{required:!0,message:"Please enter your CloudZero API key"}],children:(0,t.jsx)(v.Input.Password,{placeholder:"Enter your CloudZero API key"})}),(0,t.jsx)(k.Form.Item,{label:"Connection ID",name:"connection_id",rules:[{required:!0,message:"Please enter your CloudZero connection ID"}],children:(0,t.jsx)(v.Input,{placeholder:"Enter your CloudZero connection ID"})}),(0,t.jsx)(k.Form.Item,{label:"Timezone",name:"timezone",tooltip:"Timezone for date handling (defaults to UTC if not provided)",children:(0,t.jsx)(v.Input,{placeholder:"UTC"})})]})})}let eo=async(e,t={})=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/dry-run`:"/cloudzero/dry-run",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({limit:t.limit??10})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to perform dry run")}return await s.json()},ec=async(e,t={})=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/export`:"/cloudzero/export",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({operation:t.operation??"replace_hourly"})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to export data")}return await s.json()};var ed=e.i(127952),eu=e.i(560445),em=e.i(869216),eh=e.i(883552),ex=e.i(262218),eg=e.i(269638),ef=e.i(688511),ep=e.i(431343),ey=e.i(727612),ej=e.i(569074);function eb({open:e,onOk:a,onCancel:l,settings:s}){var r;let i,{accessToken:n}=(0,Y.default)(),[o]=k.Form.useForm(),c=(r=n||"",i=(0,K.useQueryClient)(),(0,H.useMutation)({mutationFn:async e=>{if(!r)throw Error("Access token is required");return await J(r,e)},onSuccess:()=>{i.invalidateQueries({queryKey:Q.list({})})}}));(0,b.useEffect)(()=>{e&&s?o.setFieldsValue({connection_id:s.connection_id,timezone:s.timezone||"UTC",api_key:""}):e&&o.resetFields()},[e,s,o]);let d=async()=>{try{let e=await o.validateFields();c.mutate({connection_id:e.connection_id,timezone:e.timezone||"UTC",...e.api_key&&{api_key:e.api_key}},{onSuccess:()=>{er.default.success("CloudZero integration updated successfully"),o.resetFields(),a()},onError:e=>{e?.errorFields||er.default.error(e?.message||"Failed to update CloudZero integration")}})}catch(e){if(e?.errorFields)return;er.default.error(e?.message||"Failed to update CloudZero integration")}};return(0,t.jsx)(T.Modal,{title:"Edit CloudZero Integration",open:e,onOk:d,onCancel:()=>{o.resetFields(),l()},confirmLoading:c.isPending,okText:c.isPending?"Updating...":"Update",cancelText:"Cancel",okButtonProps:{disabled:c.isPending},cancelButtonProps:{disabled:c.isPending},children:(0,t.jsxs)(k.Form,{form:o,layout:"vertical",onFinish:d,children:[(0,t.jsx)(k.Form.Item,{label:"CloudZero API Key",name:"api_key",rules:[{required:!1,message:"Please enter your CloudZero API key"}],tooltip:"Leave empty to keep the existing API key",children:(0,t.jsx)(v.Input.Password,{placeholder:"Leave empty to keep existing"})}),(0,t.jsx)(k.Form.Item,{label:"Connection ID",name:"connection_id",rules:[{required:!0,message:"Please enter your CloudZero connection ID"}],children:(0,t.jsx)(v.Input,{placeholder:"Enter your CloudZero connection ID"})}),(0,t.jsx)(k.Form.Item,{label:"Timezone",name:"timezone",tooltip:"Timezone for date handling (defaults to UTC if not provided)",children:(0,t.jsx)(v.Input,{placeholder:"UTC"})})]})})}function eC({settings:e,onSettingsUpdated:a}){var l;let s,r,i,{accessToken:n}=(0,Y.default)(),[o,c]=(0,b.useState)(!1),[d,u]=(0,b.useState)(!1),m=(s=n||"",(0,H.useMutation)({mutationFn:async(e={})=>{if(!s)throw Error("Access token is required");return await eo(s,e)}})),h=(r=n||"",(0,H.useMutation)({mutationFn:async(e={})=>{if(!r)throw Error("Access token is required");return await ec(r,e)}})),x=(l=n||"",i=(0,K.useQueryClient)(),(0,H.useMutation)({mutationFn:async()=>{if(!l)throw Error("Access token is required");return await X(l)},onSuccess:()=>{i.invalidateQueries({queryKey:Q.list({})})}})),g=m.data?JSON.stringify(m.data,null,2):null,f=async()=>{c(!1),a()};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"space-y-6 w-full max-w-4xl mx-auto",children:(0,t.jsxs)(ee.Card,{title:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"text-lg font-semibold",children:"CloudZero Configuration"}),(0,t.jsx)(ex.Tag,{color:"success",className:"ml-2 capitalize",children:e.status||"Active"})]}),extra:(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(C.Button,{icon:(0,t.jsx)(ef.Edit,{size:16}),onClick:()=>{c(!0)},className:"flex items-center gap-2",children:"Edit"}),(0,t.jsx)(C.Button,{danger:!0,icon:(0,t.jsx)(ey.Trash2,{size:16}),onClick:()=>{u(!0)},className:"flex items-center gap-2",children:"Delete"})]}),className:"shadow-xs",children:[(0,t.jsxs)(em.Descriptions,{bordered:!0,column:{xxl:1,xl:1,lg:1,md:1,sm:1,xs:1},children:[(0,t.jsx)(em.Descriptions.Item,{label:"API Key (Redacted)",children:(0,t.jsx)("span",{className:"font-mono text-gray-600",children:e.api_key_masked||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Not configured"})})}),(0,t.jsx)(em.Descriptions.Item,{label:"Connection ID",children:(0,t.jsx)("span",{className:"font-mono text-gray-600",children:e.connection_id||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Not configured"})})}),(0,t.jsx)(em.Descriptions.Item,{label:"Timezone",children:e.timezone||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Default (UTC)"})})]}),(0,t.jsx)(E.Divider,{orientation:"left",className:"text-gray-500",children:"Actions"}),(0,t.jsxs)("div",{className:"flex flex-wrap gap-4 mb-6",children:[(0,t.jsx)(C.Button,{onClick:()=>{n&&m.mutate({limit:10},{onSuccess:e=>{er.default.success("Dry run completed successfully")},onError:e=>{er.default.error(e?.message||"Failed to perform dry run")}})},loading:m.isPending,icon:(0,t.jsx)(ep.Play,{size:16}),className:"flex items-center gap-2",children:"Run Dry Run Simulation"}),(0,t.jsx)(eh.Popconfirm,{title:"Export Data to CloudZero",description:"This will push the current accumulated cost data to CloudZero. Continue?",onConfirm:()=>{n&&h.mutate({operation:"replace_hourly"},{onSuccess:()=>{er.default.success("Data successfully exported to CloudZero")},onError:e=>{er.default.error(e?.message||"Failed to export data")}})},okText:"Export",cancelText:"Cancel",children:(0,t.jsx)(C.Button,{type:"primary",loading:h.isPending,icon:(0,t.jsx)(ej.Upload,{size:16}),className:"flex items-center gap-2",children:"Export Data Now"})})]}),g&&(0,t.jsx)("div",{className:"mt-6 animate-in fade-in slide-in-from-top-4 duration-300",children:(0,t.jsx)(eu.Alert,{message:"Dry Run Results",description:(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)("p",{className:"mb-2 text-gray-600",children:["Simulation output for connection: ",e.connection_id]}),(0,t.jsx)("pre",{className:"bg-gray-50 p-4 rounded-md border border-gray-200 overflow-x-auto text-xs font-mono text-gray-800",children:g})]}),type:"info",showIcon:!0,icon:(0,t.jsx)(eg.CheckCircle,{className:"text-blue-500"})})})]})}),(0,t.jsx)(eb,{open:o,onOk:f,onCancel:()=>{c(!1)},settings:e}),(0,t.jsx)(ed.default,{isOpen:d,title:"Delete CloudZero Integration?",message:"Are you sure you want to delete this CloudZero integration? All associated settings and configurations will be permanently removed.",resourceInformationTitle:"Integration Details",resourceInformation:[{label:"Connection ID",value:e.connection_id,code:!0},{label:"Timezone",value:e.timezone||"Default (UTC)"}],onCancel:()=>{u(!1)},onOk:()=>{n&&x.mutate(void 0,{onSuccess:()=>{er.default.success("CloudZero integration deleted successfully"),u(!1),a()},onError:e=>{er.default.error(e?.message||"Failed to delete CloudZero integration")}})},confirmLoading:x.isPending})]})}function ek(){let{accessToken:e}=(0,Y.default)(),{data:a,isLoading:l,error:s}=(0,G.useQuery)({queryKey:Q.list({}),queryFn:async()=>await V(e),enabled:!!e,staleTime:36e5,gcTime:36e5}),r=(0,K.useQueryClient)(),i=(0,W.createQueryKeys)("cloudZeroSettings"),[n,o]=(0,b.useState)(!1),c=async()=>{o(!1),await r.invalidateQueries({queryKey:i.list({})})};return l?(0,t.jsx)(ee.Card,{children:(0,t.jsx)(_.Typography.Text,{children:"Loading CloudZero settings..."})}):s?(0,t.jsx)(ee.Card,{children:(0,t.jsxs)(_.Typography.Text,{className:"text-red-600",children:["Error loading CloudZero settings: ",s instanceof Error?s.message:String(s)]})}):a?(0,t.jsx)(t.Fragment,{children:(0,t.jsx)(eC,{settings:a,onSettingsUpdated:c})}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(es,{startCreation:()=>o(!0)}),(0,t.jsx)(en,{open:n,onOk:c,onCancel:()=>{o(!1)}})]})}var ev=e.i(291542),eT=e.i(335771);e.i(622826);var ew=e.i(112179),e_=e.i(902555);let eN=[{value:"success",label:"Success"},{value:"failure",label:"Failure"},{value:"success_and_failure",label:"Success & Failure"}],eS=({callbacks:e,availableCallbacks:l={},onTest:s=()=>{},onEdit:r=()=>{},onDelete:i=()=>{},onAdd:n=()=>{}})=>{let o=[{title:(0,t.jsx)("span",{className:"font-medium text-gray-700",children:"Callback Name"}),dataIndex:"name",key:"name",render:(e,a)=>{let s=a.name,r=l[s]?.ui_callback_name||s;return(0,t.jsx)("div",{className:"font-medium text-gray-800",children:r})}},{title:(0,t.jsx)("span",{className:"font-medium text-gray-700",children:"Mode"}),key:"mode",render:(e,a)=>{let l=a.type||a.mode||"success",s=eN.find(e=>e.value===l)?.label||l;return(0,t.jsx)(ew.StatusBadge,{tone:"success"===l?"success":"failure"===l?"error":"info",label:s})},width:240},{title:(0,t.jsx)("span",{className:"font-medium text-gray-700 text-right w-full block",children:"Actions"}),key:"actions",align:"right",render:(e,a)=>(0,t.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,t.jsx)(e_.default,{variant:"Test",tooltipText:"Test Callback",onClick:()=>s(a)}),(0,t.jsx)(e_.default,{variant:"Edit",tooltipText:"Edit Callback",onClick:()=>r(a)}),(0,t.jsx)(e_.default,{variant:"Delete",tooltipText:"Delete Callback",onClick:()=>i(a)})]}),width:240}];return(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{className:"w-full mt-4",children:[(0,t.jsx)(a.Button,{onClick:n,className:"mx-auto",children:"+ Add Callback"}),(0,t.jsx)("div",{className:"flex justify-between items-center my-2",children:(0,t.jsx)(eT.default,{level:4,children:"Active Logging Callbacks"})}),0===e.length?(0,t.jsx)("div",{className:"flex flex-col items-center justify-center p-8 bg-gray-50 border border-gray-200 rounded-lg",children:(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-700 mb-2",children:"No callbacks configured"}),(0,t.jsx)("p",{className:"text-gray-500",children:"Add your first callback to start logging data to external services."})]})}):(0,t.jsx)("div",{className:"bg-white border border-gray-200 rounded-lg overflow-hidden",children:(0,t.jsx)(ev.Table,{columns:o,dataSource:e,rowKey:e=>`${e.name}-${e.type||e.mode||"success"}`,pagination:!1,rowClassName:()=>"hover:bg-gray-50"})})]})})};var eE=e.i(190702);let{Title:eF,Paragraph:eI}=_.Typography,eP=({params:e,callbackConfigs:a,selectedCallback:l})=>e&&0!==e.length?(0,t.jsx)("div",{className:"space-y-4 mt-6 p-4 bg-gray-50 rounded-lg border",children:e.map(e=>{let s=a.find(e=>e.id===l),r=s?.dynamic_params?.[e]||{},i=r.type||"text",n=r.ui_name||e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),o=r.required||!1;return(0,t.jsx)(O.default,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:[n," "]}),name:e,className:"mb-4",rules:o?[{required:!0,message:`Please enter the ${n.toLowerCase()}`}]:void 0,children:"password"===i?(0,t.jsx)(v.Input.Password,{size:"large",placeholder:`Enter your ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500"}):"number"===i?(0,t.jsx)(v.Input,{type:"number",size:"large",placeholder:`Enter ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500",min:0,max:1,step:.1}):(0,t.jsx)(v.Input,{size:"large",placeholder:`Enter your ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500"})},e)})}):null,eA=({callbackConfigs:e,selectedCallback:a,onCallbackChange:l,disabled:s=!1})=>(0,t.jsx)(O.default,{label:"Callback",name:"callback",rules:s?void 0:[{required:!0,message:"Please select a callback"}],children:(0,t.jsx)(w.Select,{placeholder:"Choose a logging callback...",size:"large",className:"w-full",showSearch:!0,disabled:s,value:a,filterOption:(e,t)=>(t?.value?.toString()??"").toLowerCase().includes(e.toLowerCase()),onChange:l,children:e.map(e=>{let a=e.logo,l=(0,z.resolveLogoSrc)(a&&(a.includes("/")||a.startsWith("data:")||a.startsWith("http"))?a:`/ui/assets/logos/${a}`);return(0,t.jsx)(r.SelectItem,{value:e.id,children:(0,t.jsxs)("div",{className:"flex items-center space-x-3 py-1",children:[(0,t.jsx)("div",{className:"w-6 h-6 flex items-center justify-center",children:(0,t.jsx)("img",{src:l,alt:`${e.displayName} logo`,className:"w-6 h-6 rounded-sm object-contain",onError:e=>{e.currentTarget.style.display="none"}})}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:e.displayName})]})},e.id)})})}),eB=(e,t,a)=>{if(!e)return a?Object.keys(a):[];let l=t.find(t=>t.id===e);return l?.dynamic_params?Object.keys(l.dynamic_params):a?Object.keys(a):[]},eL=({accessToken:e,userRole:r,userID:v,premiumUser:w})=>{let[_,E]=(0,b.useState)([]),[F,I]=(0,b.useState)([]),[P,A]=(0,b.useState)(!1),[B]=k.Form.useForm(),[z]=k.Form.useForm(),[O,D]=(0,b.useState)(null),[U,Z]=(0,b.useState)(""),[R,M]=(0,b.useState)({}),[q,H]=(0,b.useState)([]),[G,K]=(0,b.useState)(!1),[W,Q]=(0,b.useState)([]),[V,J]=(0,b.useState)({}),[X,Y]=(0,b.useState)([]),[ee,et]=(0,b.useState)(!1),[ea,el]=(0,b.useState)(null),[es,er]=(0,b.useState)(!1),[ei,en]=(0,b.useState)(null),[eo,ec]=(0,b.useState)(!1),[eu,em]=(0,b.useState)(!1),[eh,ex]=(0,b.useState)(!1);(0,b.useEffect)(()=>{e&&(0,S.getCallbackConfigsCall)(e).then(e=>{Q(e||[])}).catch(e=>{N.default.fromBackend("Failed to load callback configs: "+(0,eE.parseErrorMessage)(e))})},[e]),(0,b.useEffect)(()=>{if(ee&&ea){let e=Object.fromEntries(Object.entries(ea.variables||{}).map(([e,t])=>[e,t??""]));z.setFieldsValue({...e,callback:ea.name})}},[ee,ea,z]);let eg=e=>{q.includes(e)?H(q.filter(t=>t!==e)):H([...q,e])},ef={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,b.useEffect)(()=>{e&&r&&v&&(0,S.getCallbacksCall)(e,v,r).then(e=>{E(e.callbacks),J(e.available_callbacks);let t=e.alerts;if(t&&t.length>0){let e=t[0],a=e.variables.SLACK_WEBHOOK_URL;H(e.active_alerts),Z(a),M(e.alerts_to_webhook)}I(t)})},[e,r,v]);let ep=e=>q&&q.includes(e),ey=async(t,a,l)=>{if(e){l?ec(!0):em(!0);try{if(await (0,S.setCallbacksCall)(e,{environment_variables:t,litellm_settings:{success_callback:[a]}}),N.default.success(l?"Callback updated successfully":`Callback ${a} added successfully`),l?(et(!1),z.resetFields(),el(null)):(K(!1),B.resetFields(),D(null),Y([])),v&&r){let t=await (0,S.getCallbacksCall)(e,v,r);E(t.callbacks)}}catch(e){N.default.fromBackend(e)}finally{l?ec(!1):em(!1)}}},ej=async e=>{ea&&await ey(e,ea.name,!0)},eb=async e=>{let t=e?.callback;t&&await ey(e,t,!1)},eC=async()=>{if(!e)return;let t={};Object.entries(ef).forEach(([e,a])=>{let l=document.querySelector(`input[name="${e}"]`),s=l?.value||"";t[e]=s});try{await (0,S.setCallbacksCall)(e,{general_settings:{alert_to_webhook_url:t,alert_types:q}})}catch(e){N.default.fromBackend(e)}N.default.success("Alerts updated successfully")},ev=async()=>{if(ei&&e)try{if(ex(!0),await (0,S.deleteCallback)(e,ei.name),N.default.success(`Callback ${ei.name} deleted successfully`),v&&r){let t=await (0,S.getCallbacksCall)(e,v,r);E(t.callbacks)}er(!1),en(null)}catch(e){console.error("Failed to delete callback:",e),N.default.fromBackend(e)}finally{ex(!1)}};return e?(0,t.jsxs)("div",{className:"w-full mx-4",children:[(0,t.jsx)(s.Grid,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,t.jsxs)(o.TabGroup,{children:[(0,t.jsxs)(g.TabList,{variant:"line",defaultValue:"1",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Logging Callbacks"}),(0,t.jsx)(n.Tab,{value:"2",children:"CloudZero Cost Tracking"}),(0,t.jsx)(n.Tab,{value:"2",children:"Alerting Types"}),(0,t.jsx)(n.Tab,{value:"3",children:"Alerting Settings"}),(0,t.jsx)(n.Tab,{value:"4",children:"Email Alerts"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)(eS,{callbacks:_,availableCallbacks:V,onAdd:()=>K(!0),onEdit:e=>{el(e),et(!0)},onDelete:e=>{en(e),er(!0)},onTest:async t=>{try{await (0,S.serviceHealthCheck)(e,t.name),N.default.success("Health check triggered")}catch(e){N.default.fromBackend((0,eE.parseErrorMessage)(e))}}})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)("div",{className:"p-8",children:(0,t.jsx)(ek,{})})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsxs)(l.Card,{children:[(0,t.jsxs)(y.Text,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,t.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,t.jsxs)(c.Table,{children:[(0,t.jsx)(m.TableHead,{children:(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(h.TableHeaderCell,{}),(0,t.jsx)(h.TableHeaderCell,{}),(0,t.jsx)(h.TableHeaderCell,{children:"Slack Webhook URL"})]})}),(0,t.jsx)(d.TableBody,{children:Object.entries(ef).map(([e,l],s)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(u.TableCell,{children:"region_outage_alerts"==e?w?(0,t.jsx)(i.Switch,{id:"switch",name:"switch",checked:ep(e),onChange:()=>eg(e)}):(0,t.jsx)(a.Button,{className:"flex items-center justify-center",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,t.jsx)(i.Switch,{id:"switch",name:"switch",checked:ep(e),onChange:()=>eg(e)})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(y.Text,{children:l})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(j.TextInput,{name:e,type:"password",defaultValue:R&&R[e]?R[e]:U})})]},s))})]}),(0,t.jsx)(a.Button,{size:"xs",className:"mt-2",onClick:eC,children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:async()=>{try{await (0,S.serviceHealthCheck)(e,"slack"),N.default.success("Alert test triggered. Test request to slack made - check logs/alerts on slack to verify")}catch(e){N.default.fromBackend((0,eE.parseErrorMessage)(e))}},className:"mx-2",children:"Test Alerts"})]})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)($,{accessToken:e,premiumUser:w})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)(L,{accessToken:e,premiumUser:w,alerts:F})})]})]})}),(0,t.jsxs)(T.Modal,{title:"Add Logging Callback",open:G,width:800,onCancel:()=>{K(!1),D(null),Y([])},footer:null,children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:[" ","LiteLLM Docs: Logging"]}),(0,t.jsxs)(k.Form,{form:B,onFinish:eb,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsx)(eA,{callbackConfigs:W,selectedCallback:O,onCallbackChange:e=>{D(e),Y(eB(e,W))}}),(0,t.jsx)(eP,{params:X,callbackConfigs:W,selectedCallback:O}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-6 mt-6 border-t border-gray-200",children:[(0,t.jsx)(C.Button,{onClick:()=>{K(!1),D(null),Y([]),B.resetFields()},disabled:eu,children:"Cancel"}),(0,t.jsx)(C.Button,{htmlType:"submit",loading:eu,disabled:eu,children:eu?"Adding...":"Add Callback"})]})]})]}),(0,t.jsx)(T.Modal,{open:ee,width:800,title:"Edit Callback Settings",onCancel:()=>{et(!1),el(null),z.resetFields()},footer:null,children:(0,t.jsxs)(k.Form,{form:z,onFinish:ej,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[ea&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eA,{callbackConfigs:W,selectedCallback:ea.name,onCallbackChange:()=>{},disabled:!0}),(0,t.jsx)(eP,{params:eB(ea.name,W,ea.variables),callbackConfigs:W,selectedCallback:ea.name})]}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-6 mt-6 border-t border-gray-200",children:[(0,t.jsx)(C.Button,{onClick:()=>{et(!1),el(null),z.resetFields()},disabled:eo,children:"Cancel"}),(0,t.jsx)(C.Button,{onClick:()=>{z.submit()},loading:eo,disabled:eo,children:eo?"Saving...":"Save Changes"})]})]})}),(0,t.jsx)(ed.default,{isOpen:es,title:"Delete Callback",message:"Are you sure you want to delete this callback? This action cannot be undone.",resourceInformationTitle:"Callback Information",resourceInformation:[{label:"Callback Name",value:ei?.name},{label:"Mode",value:ei?.mode||"success"}],onCancel:()=>{er(!1),en(null)},onOk:ev,confirmLoading:eh})]}):null};e.s(["default",0,function(){let{accessToken:e,userRole:a,userId:l,premiumUser:s}=(0,Y.default)();return(0,t.jsx)(eL,{userID:l,userRole:a,accessToken:e,premiumUser:s})}],372024)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/003_1s9xbht43.js b/litellm/proxy/_experimental/out/_next/static/chunks/003_1s9xbht43.js new file mode 100644 index 00000000000..74f24e425e0 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/003_1s9xbht43.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,115504,207670,e=>{"use strict";function r(){for(var e,r,o=0,t="",l=arguments.length;o"boolean"==typeof e?`${e}`:0===e?"0":e,t=e=>{let t=function(){for(var o,t,l=arguments.length,a=Array(l),n=0;n{let o=Object.fromEntries(Object.entries(e||{}).filter(e=>{let[r]=e;return!["class","className"].includes(r)}));return t(r.map(e=>e(o)),null==e?void 0:e.class,null==e?void 0:e.className)}},cva:e=>r=>{var l;if((null==e?void 0:e.variants)==null)return t(null==e?void 0:e.base,null==r?void 0:r.class,null==r?void 0:r.className);let{variants:a,defaultVariants:n}=e,s=Object.keys(a).map(e=>{let t=null==r?void 0:r[e],l=null==n?void 0:n[e],s=o(t)||o(l);return a[e][s]}),i={...n,...r&&Object.entries(r).reduce((e,r)=>{let[o,t]=r;return void 0===t?e:{...e,[o]:t}},{})},d=null==e||null==(l=e.compoundVariants)?void 0:l.reduce((e,r)=>{let{class:o,className:t,...l}=r;return Object.entries(l).every(e=>{let[r,o]=e,t=i[r];return Array.isArray(o)?o.includes(t):t===o})?[...e,o,t]:e},[]);return t(null==e?void 0:e.base,s,d,null==r?void 0:r.class,null==r?void 0:r.className)},cx:t}},{compose:l,cva:a,cx:n}=t(),s=(e=new Map,r=null,o)=>({nextPart:e,validators:r,classGroupId:o}),i=[],d=(e,r,o)=>{if(0==e.length-r)return o.classGroupId;let t=e[r],l=o.nextPart.get(t);if(l){let o=d(e,r+1,l);if(o)return o}let a=o.validators;if(null===a)return;let n=0===r?e.join("-"):e.slice(r).join("-"),s=a.length;for(let e=0;e{let o=s();for(let t in e)m(e[t],o,t,r);return o},m=(e,r,o,t)=>{let l=e.length;for(let a=0;a{"string"==typeof e?u(e,r,o):"function"==typeof e?b(e,r,o,t):f(e,r,o,t)},u=(e,r,o)=>{(""===e?r:g(r,e)).classGroupId=o},b=(e,r,o,t)=>{h(e)?m(e(t),r,o,t):(null===r.validators&&(r.validators=[]),r.validators.push({classGroupId:o,validator:e}))},f=(e,r,o,t)=>{let l=Object.entries(e),a=l.length;for(let e=0;e{let o=e,t=r.split("-"),l=t.length;for(let e=0;e"isThemeGetter"in e&&!0===e.isThemeGetter,k=[],x=(e,r,o,t,l)=>({modifiers:e,hasImportantModifier:r,baseClassName:o,maybePostfixModifierPosition:t,isExternal:l}),v=/\s+/,w=e=>{let r;if("string"==typeof e)return e;let o="";for(let t=0;t{let r=r=>r[e]||y;return r.isThemeGetter=!0,r},j=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,O=/^\((?:(\w[\w-]*):)?(.+)\)$/i,N=/^\d+\/\d+$/,C=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,G=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,A=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,$=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,I=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,T=e=>N.test(e),M=e=>!!e&&!Number.isNaN(Number(e)),W=e=>!!e&&Number.isInteger(Number(e)),P=e=>e.endsWith("%")&&M(e.slice(0,-1)),S=e=>C.test(e),q=()=>!0,B=e=>G.test(e)&&!A.test(e),E=()=>!1,K=e=>$.test(e),R=e=>I.test(e),U=e=>!V(e)&&!Q(e),_=e=>et(e,es,E),V=e=>j.test(e),D=e=>et(e,ei,B),F=e=>et(e,ed,M),H=e=>et(e,ea,E),J=e=>et(e,en,R),L=e=>et(e,em,K),Q=e=>O.test(e),X=e=>el(e,ei),Y=e=>el(e,ec),Z=e=>el(e,ea),ee=e=>el(e,es),er=e=>el(e,en),eo=e=>el(e,em,!0),et=(e,r,o)=>{let t=j.exec(e);return!!t&&(t[1]?r(t[1]):o(t[2]))},el=(e,r,o=!1)=>{let t=O.exec(e);return!!t&&(t[1]?r(t[1]):o)},ea=e=>"position"===e||"percentage"===e,en=e=>"image"===e||"url"===e,es=e=>"length"===e||"size"===e||"bg-size"===e,ei=e=>"length"===e,ed=e=>"number"===e,ec=e=>"family-name"===e,em=e=>"shadow"===e,ep=((e,...r)=>{let o,t,l,a,n=e=>{let r=t(e);if(r)return r;let a=((e,r)=>{let{parseClassName:o,getClassGroupId:t,getConflictingClassGroupIds:l,sortModifiers:a}=r,n=[],s=e.trim().split(v),i="";for(let e=s.length-1;e>=0;e-=1){let r=s[e],{isExternal:d,modifiers:c,hasImportantModifier:m,baseClassName:p,maybePostfixModifierPosition:u}=o(r);if(d){i=r+(i.length>0?" "+i:i);continue}let b=!!u,f=t(b?p.substring(0,u):p);if(!f){if(!b||!(f=t(p))){i=r+(i.length>0?" "+i:i);continue}b=!1}let g=0===c.length?"":1===c.length?c[0]:a(c).join(":"),h=m?g+"!":g,k=h+f;if(n.indexOf(k)>-1)continue;n.push(k);let x=l(f,b);for(let e=0;e0?" "+i:i)}return i})(e,o);return l(e,a),a};return a=s=>{var m;let p;return t=(o={cache:(e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let r=0,o=Object.create(null),t=Object.create(null),l=(l,a)=>{o[l]=a,++r>e&&(r=0,t=o,o=Object.create(null))};return{get(e){let r=o[e];return void 0!==r?r:void 0!==(r=t[e])?(l(e,r),r):void 0},set(e,r){e in o?o[e]=r:l(e,r)}}})((m=r.reduce((e,r)=>r(e),e())).cacheSize),parseClassName:(e=>{let{prefix:r,experimentalParseClassName:o}=e,t=e=>{let r,o=[],t=0,l=0,a=0,n=e.length;for(let s=0;sa?r-a:void 0)};if(r){let e=r+":",o=t;t=r=>r.startsWith(e)?o(r.slice(e.length)):x(k,!1,r,void 0,!0)}if(o){let e=t;t=r=>o({className:r,parseClassName:e})}return t})(m),sortModifiers:(p=new Map,m.orderSensitiveModifiers.forEach((e,r)=>{p.set(e,1e6+r)}),e=>{let r=[],o=[];for(let t=0;t0&&(o.sort(),r.push(...o),o=[]),r.push(l)):o.push(l)}return o.length>0&&(o.sort(),r.push(...o)),r}),...(e=>{let r=(e=>{let{theme:r,classGroups:o}=e;return c(o,r)})(e),{conflictingClassGroups:o,conflictingClassGroupModifiers:t}=e;return{getClassGroupId:e=>{if(e.startsWith("[")&&e.endsWith("]")){var o;let r,t,l;return -1===(o=e).slice(1,-1).indexOf(":")?void 0:(t=(r=o.slice(1,-1)).indexOf(":"),(l=r.slice(0,t))?"arbitrary.."+l:void 0)}let t=e.split("-"),l=+(""===t[0]&&t.length>1);return d(t,l,r)},getConflictingClassGroupIds:(e,r)=>{if(r){let r=t[e],l=o[e];if(r){if(l){let e=Array(l.length+r.length);for(let r=0;ra(((...e)=>{let r,o,t=0,l="";for(;t{let e=z("color"),r=z("font"),o=z("text"),t=z("font-weight"),l=z("tracking"),a=z("leading"),n=z("breakpoint"),s=z("container"),i=z("spacing"),d=z("radius"),c=z("shadow"),m=z("inset-shadow"),p=z("text-shadow"),u=z("drop-shadow"),b=z("blur"),f=z("perspective"),g=z("aspect"),h=z("ease"),k=z("animate"),x=()=>["auto","avoid","all","avoid-page","page","left","right","column"],v=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],w=()=>[...v(),Q,V],y=()=>["auto","hidden","clip","visible","scroll"],j=()=>["auto","contain","none"],O=()=>[Q,V,i],N=()=>[T,"full","auto",...O()],C=()=>[W,"none","subgrid",Q,V],G=()=>["auto",{span:["full",W,Q,V]},W,Q,V],A=()=>[W,"auto",Q,V],$=()=>["auto","min","max","fr",Q,V],I=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],B=()=>["start","end","center","stretch","center-safe","end-safe"],E=()=>["auto",...O()],K=()=>[T,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...O()],R=()=>[e,Q,V],et=()=>[...v(),Z,H,{position:[Q,V]}],el=()=>["no-repeat",{repeat:["","x","y","space","round"]}],ea=()=>["auto","cover","contain",ee,_,{size:[Q,V]}],en=()=>[P,X,D],es=()=>["","none","full",d,Q,V],ei=()=>["",M,X,D],ed=()=>["solid","dashed","dotted","double"],ec=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],em=()=>[M,P,Z,H],ep=()=>["","none",b,Q,V],eu=()=>["none",M,Q,V],eb=()=>["none",M,Q,V],ef=()=>[M,Q,V],eg=()=>[T,"full",...O()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[S],breakpoint:[S],color:[q],container:[S],"drop-shadow":[S],ease:["in","out","in-out"],font:[U],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[S],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[S],shadow:[S],spacing:["px",M],text:[S],"text-shadow":[S],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",T,V,Q,g]}],container:["container"],columns:[{columns:[M,V,Q,s]}],"break-after":[{"break-after":x()}],"break-before":[{"break-before":x()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:w()}],overflow:[{overflow:y()}],"overflow-x":[{"overflow-x":y()}],"overflow-y":[{"overflow-y":y()}],overscroll:[{overscroll:j()}],"overscroll-x":[{"overscroll-x":j()}],"overscroll-y":[{"overscroll-y":j()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:N()}],"inset-x":[{"inset-x":N()}],"inset-y":[{"inset-y":N()}],start:[{start:N()}],end:[{end:N()}],top:[{top:N()}],right:[{right:N()}],bottom:[{bottom:N()}],left:[{left:N()}],visibility:["visible","invisible","collapse"],z:[{z:[W,"auto",Q,V]}],basis:[{basis:[T,"full","auto",s,...O()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[M,T,"auto","initial","none",V]}],grow:[{grow:["",M,Q,V]}],shrink:[{shrink:["",M,Q,V]}],order:[{order:[W,"first","last","none",Q,V]}],"grid-cols":[{"grid-cols":C()}],"col-start-end":[{col:G()}],"col-start":[{"col-start":A()}],"col-end":[{"col-end":A()}],"grid-rows":[{"grid-rows":C()}],"row-start-end":[{row:G()}],"row-start":[{"row-start":A()}],"row-end":[{"row-end":A()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":$()}],"auto-rows":[{"auto-rows":$()}],gap:[{gap:O()}],"gap-x":[{"gap-x":O()}],"gap-y":[{"gap-y":O()}],"justify-content":[{justify:[...I(),"normal"]}],"justify-items":[{"justify-items":[...B(),"normal"]}],"justify-self":[{"justify-self":["auto",...B()]}],"align-content":[{content:["normal",...I()]}],"align-items":[{items:[...B(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...B(),{baseline:["","last"]}]}],"place-content":[{"place-content":I()}],"place-items":[{"place-items":[...B(),"baseline"]}],"place-self":[{"place-self":["auto",...B()]}],p:[{p:O()}],px:[{px:O()}],py:[{py:O()}],ps:[{ps:O()}],pe:[{pe:O()}],pt:[{pt:O()}],pr:[{pr:O()}],pb:[{pb:O()}],pl:[{pl:O()}],m:[{m:E()}],mx:[{mx:E()}],my:[{my:E()}],ms:[{ms:E()}],me:[{me:E()}],mt:[{mt:E()}],mr:[{mr:E()}],mb:[{mb:E()}],ml:[{ml:E()}],"space-x":[{"space-x":O()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":O()}],"space-y-reverse":["space-y-reverse"],size:[{size:K()}],w:[{w:[s,"screen",...K()]}],"min-w":[{"min-w":[s,"screen","none",...K()]}],"max-w":[{"max-w":[s,"screen","none","prose",{screen:[n]},...K()]}],h:[{h:["screen","lh",...K()]}],"min-h":[{"min-h":["screen","lh","none",...K()]}],"max-h":[{"max-h":["screen","lh",...K()]}],"font-size":[{text:["base",o,X,D]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[t,Q,F]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",P,V]}],"font-family":[{font:[Y,V,r]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[l,Q,V]}],"line-clamp":[{"line-clamp":[M,"none",Q,F]}],leading:[{leading:[a,...O()]}],"list-image":[{"list-image":["none",Q,V]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",Q,V]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:R()}],"text-color":[{text:R()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ed(),"wavy"]}],"text-decoration-thickness":[{decoration:[M,"from-font","auto",Q,D]}],"text-decoration-color":[{decoration:R()}],"underline-offset":[{"underline-offset":[M,"auto",Q,V]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:O()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",Q,V]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",Q,V]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:et()}],"bg-repeat":[{bg:el()}],"bg-size":[{bg:ea()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},W,Q,V],radial:["",Q,V],conic:[W,Q,V]},er,J]}],"bg-color":[{bg:R()}],"gradient-from-pos":[{from:en()}],"gradient-via-pos":[{via:en()}],"gradient-to-pos":[{to:en()}],"gradient-from":[{from:R()}],"gradient-via":[{via:R()}],"gradient-to":[{to:R()}],rounded:[{rounded:es()}],"rounded-s":[{"rounded-s":es()}],"rounded-e":[{"rounded-e":es()}],"rounded-t":[{"rounded-t":es()}],"rounded-r":[{"rounded-r":es()}],"rounded-b":[{"rounded-b":es()}],"rounded-l":[{"rounded-l":es()}],"rounded-ss":[{"rounded-ss":es()}],"rounded-se":[{"rounded-se":es()}],"rounded-ee":[{"rounded-ee":es()}],"rounded-es":[{"rounded-es":es()}],"rounded-tl":[{"rounded-tl":es()}],"rounded-tr":[{"rounded-tr":es()}],"rounded-br":[{"rounded-br":es()}],"rounded-bl":[{"rounded-bl":es()}],"border-w":[{border:ei()}],"border-w-x":[{"border-x":ei()}],"border-w-y":[{"border-y":ei()}],"border-w-s":[{"border-s":ei()}],"border-w-e":[{"border-e":ei()}],"border-w-t":[{"border-t":ei()}],"border-w-r":[{"border-r":ei()}],"border-w-b":[{"border-b":ei()}],"border-w-l":[{"border-l":ei()}],"divide-x":[{"divide-x":ei()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ei()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...ed(),"hidden","none"]}],"divide-style":[{divide:[...ed(),"hidden","none"]}],"border-color":[{border:R()}],"border-color-x":[{"border-x":R()}],"border-color-y":[{"border-y":R()}],"border-color-s":[{"border-s":R()}],"border-color-e":[{"border-e":R()}],"border-color-t":[{"border-t":R()}],"border-color-r":[{"border-r":R()}],"border-color-b":[{"border-b":R()}],"border-color-l":[{"border-l":R()}],"divide-color":[{divide:R()}],"outline-style":[{outline:[...ed(),"none","hidden"]}],"outline-offset":[{"outline-offset":[M,Q,V]}],"outline-w":[{outline:["",M,X,D]}],"outline-color":[{outline:R()}],shadow:[{shadow:["","none",c,eo,L]}],"shadow-color":[{shadow:R()}],"inset-shadow":[{"inset-shadow":["none",m,eo,L]}],"inset-shadow-color":[{"inset-shadow":R()}],"ring-w":[{ring:ei()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:R()}],"ring-offset-w":[{"ring-offset":[M,D]}],"ring-offset-color":[{"ring-offset":R()}],"inset-ring-w":[{"inset-ring":ei()}],"inset-ring-color":[{"inset-ring":R()}],"text-shadow":[{"text-shadow":["none",p,eo,L]}],"text-shadow-color":[{"text-shadow":R()}],opacity:[{opacity:[M,Q,V]}],"mix-blend":[{"mix-blend":[...ec(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":ec()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[M]}],"mask-image-linear-from-pos":[{"mask-linear-from":em()}],"mask-image-linear-to-pos":[{"mask-linear-to":em()}],"mask-image-linear-from-color":[{"mask-linear-from":R()}],"mask-image-linear-to-color":[{"mask-linear-to":R()}],"mask-image-t-from-pos":[{"mask-t-from":em()}],"mask-image-t-to-pos":[{"mask-t-to":em()}],"mask-image-t-from-color":[{"mask-t-from":R()}],"mask-image-t-to-color":[{"mask-t-to":R()}],"mask-image-r-from-pos":[{"mask-r-from":em()}],"mask-image-r-to-pos":[{"mask-r-to":em()}],"mask-image-r-from-color":[{"mask-r-from":R()}],"mask-image-r-to-color":[{"mask-r-to":R()}],"mask-image-b-from-pos":[{"mask-b-from":em()}],"mask-image-b-to-pos":[{"mask-b-to":em()}],"mask-image-b-from-color":[{"mask-b-from":R()}],"mask-image-b-to-color":[{"mask-b-to":R()}],"mask-image-l-from-pos":[{"mask-l-from":em()}],"mask-image-l-to-pos":[{"mask-l-to":em()}],"mask-image-l-from-color":[{"mask-l-from":R()}],"mask-image-l-to-color":[{"mask-l-to":R()}],"mask-image-x-from-pos":[{"mask-x-from":em()}],"mask-image-x-to-pos":[{"mask-x-to":em()}],"mask-image-x-from-color":[{"mask-x-from":R()}],"mask-image-x-to-color":[{"mask-x-to":R()}],"mask-image-y-from-pos":[{"mask-y-from":em()}],"mask-image-y-to-pos":[{"mask-y-to":em()}],"mask-image-y-from-color":[{"mask-y-from":R()}],"mask-image-y-to-color":[{"mask-y-to":R()}],"mask-image-radial":[{"mask-radial":[Q,V]}],"mask-image-radial-from-pos":[{"mask-radial-from":em()}],"mask-image-radial-to-pos":[{"mask-radial-to":em()}],"mask-image-radial-from-color":[{"mask-radial-from":R()}],"mask-image-radial-to-color":[{"mask-radial-to":R()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":v()}],"mask-image-conic-pos":[{"mask-conic":[M]}],"mask-image-conic-from-pos":[{"mask-conic-from":em()}],"mask-image-conic-to-pos":[{"mask-conic-to":em()}],"mask-image-conic-from-color":[{"mask-conic-from":R()}],"mask-image-conic-to-color":[{"mask-conic-to":R()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:et()}],"mask-repeat":[{mask:el()}],"mask-size":[{mask:ea()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",Q,V]}],filter:[{filter:["","none",Q,V]}],blur:[{blur:ep()}],brightness:[{brightness:[M,Q,V]}],contrast:[{contrast:[M,Q,V]}],"drop-shadow":[{"drop-shadow":["","none",u,eo,L]}],"drop-shadow-color":[{"drop-shadow":R()}],grayscale:[{grayscale:["",M,Q,V]}],"hue-rotate":[{"hue-rotate":[M,Q,V]}],invert:[{invert:["",M,Q,V]}],saturate:[{saturate:[M,Q,V]}],sepia:[{sepia:["",M,Q,V]}],"backdrop-filter":[{"backdrop-filter":["","none",Q,V]}],"backdrop-blur":[{"backdrop-blur":ep()}],"backdrop-brightness":[{"backdrop-brightness":[M,Q,V]}],"backdrop-contrast":[{"backdrop-contrast":[M,Q,V]}],"backdrop-grayscale":[{"backdrop-grayscale":["",M,Q,V]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[M,Q,V]}],"backdrop-invert":[{"backdrop-invert":["",M,Q,V]}],"backdrop-opacity":[{"backdrop-opacity":[M,Q,V]}],"backdrop-saturate":[{"backdrop-saturate":[M,Q,V]}],"backdrop-sepia":[{"backdrop-sepia":["",M,Q,V]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":O()}],"border-spacing-x":[{"border-spacing-x":O()}],"border-spacing-y":[{"border-spacing-y":O()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",Q,V]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[M,"initial",Q,V]}],ease:[{ease:["linear","initial",h,Q,V]}],delay:[{delay:[M,Q,V]}],animate:[{animate:["none",k,Q,V]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[f,Q,V]}],"perspective-origin":[{"perspective-origin":w()}],rotate:[{rotate:eu()}],"rotate-x":[{"rotate-x":eu()}],"rotate-y":[{"rotate-y":eu()}],"rotate-z":[{"rotate-z":eu()}],scale:[{scale:eb()}],"scale-x":[{"scale-x":eb()}],"scale-y":[{"scale-y":eb()}],"scale-z":[{"scale-z":eb()}],"scale-3d":["scale-3d"],skew:[{skew:ef()}],"skew-x":[{"skew-x":ef()}],"skew-y":[{"skew-y":ef()}],transform:[{transform:[Q,V,"","none","gpu","cpu"]}],"transform-origin":[{origin:w()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:eg()}],"translate-x":[{"translate-x":eg()}],"translate-y":[{"translate-y":eg()}],"translate-z":[{"translate-z":eg()}],"translate-none":["translate-none"],accent:[{accent:R()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:R()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",Q,V]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":O()}],"scroll-mx":[{"scroll-mx":O()}],"scroll-my":[{"scroll-my":O()}],"scroll-ms":[{"scroll-ms":O()}],"scroll-me":[{"scroll-me":O()}],"scroll-mt":[{"scroll-mt":O()}],"scroll-mr":[{"scroll-mr":O()}],"scroll-mb":[{"scroll-mb":O()}],"scroll-ml":[{"scroll-ml":O()}],"scroll-p":[{"scroll-p":O()}],"scroll-px":[{"scroll-px":O()}],"scroll-py":[{"scroll-py":O()}],"scroll-ps":[{"scroll-ps":O()}],"scroll-pe":[{"scroll-pe":O()}],"scroll-pt":[{"scroll-pt":O()}],"scroll-pr":[{"scroll-pr":O()}],"scroll-pb":[{"scroll-pb":O()}],"scroll-pl":[{"scroll-pl":O()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",Q,V]}],fill:[{fill:["none",...R()]}],"stroke-w":[{stroke:[M,X,D,F]}],stroke:[{stroke:["none",...R()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}}),{cva:eu,cx:eb,compose:ef}=t({hooks:{onComplete:e=>ep(e)}});e.s(["cn",0,eb,"cva",0,eu,"cx",0,eb],115504)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/00fcsizkvc4hx.js b/litellm/proxy/_experimental/out/_next/static/chunks/00fcsizkvc4hx.js deleted file mode 100644 index 7875ab8fb17..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/00fcsizkvc4hx.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),s=e.i(480731),n=e.i(444755),l=e.i(673706),o=e.i(95779);let i={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},c={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},d={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,l.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:h,variant:p="simple",tooltip:g,size:f=s.Sizes.SM,color:x,className:b}=e,v=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),y=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,l.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,l.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,l.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,l.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,l.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,l.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,n.tremorTwMerge)((0,l.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,l.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,l.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,l.getColorClassNames)(t,o.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,n.tremorTwMerge)((0,l.getColorClassNames)(t,o.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,x),{tooltipProps:C,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,l.mergeRefs)([m,C.refs.setReference]),className:(0,n.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",y.bgColor,y.textColor,y.borderColor,y.ringColor,d[p].rounded,d[p].border,d[p].shadow,d[p].ring,i[f].paddingX,i[f].paddingY,b)},w,v),r.default.createElement(a.default,Object.assign({text:g},C)),r.default.createElement(h,{className:(0,n.tremorTwMerge)(u("icon"),"shrink-0",c[f].height,c[f].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},439189,435684,96226,497245,e=>{"use strict";function t(e){let t=Object.prototype.toString.call(e);return e instanceof Date||"object"==typeof e&&"[object Date]"===t?new e.constructor(+e):new Date("number"==typeof e||"[object Number]"===t||"string"==typeof e||"[object String]"===t?e:NaN)}function r(e,t){return e instanceof Date?new e.constructor(t):new Date(t)}e.s(["toDate",0,t],435684),e.s(["constructFrom",0,r],96226),e.s(["addDays",0,function(e,a){let s=t(e);return isNaN(a)?r(e,NaN):(a&&s.setDate(s.getDate()+a),s)}],439189),e.s(["addMonths",0,function(e,a){let s=t(e);if(isNaN(a))return r(e,NaN);if(!a)return s;let n=s.getDate(),l=r(e,s.getTime());return(l.setMonth(s.getMonth()+a+1,0),n>=l.getDate())?l:(s.setFullYear(l.getFullYear(),l.getMonth(),n),s)}],497245)},888288,e=>{"use strict";var t=e.i(271645);e.s(["default",0,(e,r)=>{let a=void 0!==r,[s,n]=(0,t.useState)(e);return[a?r:s,e=>{a||n(e)}]}])},37091,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),s=e.i(673706),n=e.i(271645);let l=n.default.forwardRef((e,l)=>{let{color:o,children:i,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return n.default.createElement("p",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o?(0,s.getColorClassNames)(o,r.colorPalette.lightText).textColor:"text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis",c)},d),i)});l.displayName="Subtitle",e.s(["Subtitle",0,l],37091)},757440,e=>{"use strict";var t=e.i(290571),r=e.i(271645);e.s(["default",0,e=>{var a=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},a),r.default.createElement("path",{d:"M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"}))}])},446428,854056,e=>{"use strict";let t;var r=e.i(290571),a=e.i(271645);e.s(["default",0,e=>{var t=(0,r.__rest)(e,[]);return a.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),a.default.createElement("path",{d:"M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 10.5858L9.17157 7.75736L7.75736 9.17157L10.5858 12L7.75736 14.8284L9.17157 16.2426L12 13.4142L14.8284 16.2426L16.2426 14.8284L13.4142 12L16.2426 9.17157L14.8284 7.75736L12 10.5858Z"}))}],446428);var s=e.i(746725),n=e.i(914189),l=e.i(553521),o=e.i(835696),i=e.i(941444),c=e.i(178677),d=e.i(294316),u=e.i(83733),m=e.i(233137),h=e.i(732607),p=e.i(397701),g=e.i(700020);function f(e){var t;return!!(e.enter||e.enterFrom||e.enterTo||e.leave||e.leaveFrom||e.leaveTo)||(null!=(t=e.as)?t:w)!==a.Fragment||1===a.default.Children.count(e.children)}let x=(0,a.createContext)(null);x.displayName="TransitionContext";var b=((t=b||{}).Visible="visible",t.Hidden="hidden",t);let v=(0,a.createContext)(null);function y(e){return"children"in e?y(e.children):e.current.filter(({el:e})=>null!==e.current).filter(({state:e})=>"visible"===e).length>0}function C(e,t){let r=(0,i.useLatestValue)(e),o=(0,a.useRef)([]),c=(0,l.useIsMounted)(),d=(0,s.useDisposables)(),u=(0,n.useEvent)((e,t=g.RenderStrategy.Hidden)=>{let a=o.current.findIndex(({el:t})=>t===e);-1!==a&&((0,p.match)(t,{[g.RenderStrategy.Unmount](){o.current.splice(a,1)},[g.RenderStrategy.Hidden](){o.current[a].state="hidden"}}),d.microTask(()=>{var e;!y(o)&&c.current&&(null==(e=r.current)||e.call(r))}))}),m=(0,n.useEvent)(e=>{let t=o.current.find(({el:t})=>t===e);return t?"visible"!==t.state&&(t.state="visible"):o.current.push({el:e,state:"visible"}),()=>u(e,g.RenderStrategy.Unmount)}),h=(0,a.useRef)([]),f=(0,a.useRef)(Promise.resolve()),x=(0,a.useRef)({enter:[],leave:[]}),b=(0,n.useEvent)((e,r,a)=>{h.current.splice(0),t&&(t.chains.current[r]=t.chains.current[r].filter(([t])=>t!==e)),null==t||t.chains.current[r].push([e,new Promise(e=>{h.current.push(e)})]),null==t||t.chains.current[r].push([e,new Promise(e=>{Promise.all(x.current[r].map(([e,t])=>t)).then(()=>e())})]),"enter"===r?f.current=f.current.then(()=>null==t?void 0:t.wait.current).then(()=>a(r)):a(r)}),v=(0,n.useEvent)((e,t,r)=>{Promise.all(x.current[t].splice(0).map(([e,t])=>t)).then(()=>{var e;null==(e=h.current.shift())||e()}).then(()=>r(t))});return(0,a.useMemo)(()=>({children:o,register:m,unregister:u,onStart:b,onStop:v,wait:f,chains:x}),[m,u,o,b,v,x,f])}v.displayName="NestingContext";let w=a.Fragment,N=g.RenderFeatures.RenderStrategy,k=(0,g.forwardRefWithAs)(function(e,t){let{show:r,appear:s=!1,unmount:l=!0,...i}=e,u=(0,a.useRef)(null),h=f(e),p=(0,d.useSyncRefs)(...h?[u,t]:null===t?[]:[t]);(0,c.useServerHandoffComplete)();let b=(0,m.useOpenClosed)();if(void 0===r&&null!==b&&(r=(b&m.State.Open)===m.State.Open),void 0===r)throw Error("A is used but it is missing a `show={true | false}` prop.");let[w,k]=(0,a.useState)(r?"visible":"hidden"),S=C(()=>{r||k("hidden")}),[T,_]=(0,a.useState)(!0),E=(0,a.useRef)([r]);(0,o.useIsoMorphicEffect)(()=>{!1!==T&&E.current[E.current.length-1]!==r&&(E.current.push(r),_(!1))},[E,r]);let M=(0,a.useMemo)(()=>({show:r,appear:s,initial:T}),[r,s,T]);(0,o.useIsoMorphicEffect)(()=>{r?k("visible"):y(S)||null===u.current||k("hidden")},[r,S]);let R={unmount:l},P=(0,n.useEvent)(()=>{var t;T&&_(!1),null==(t=e.beforeEnter)||t.call(e)}),L=(0,n.useEvent)(()=>{var t;T&&_(!1),null==(t=e.beforeLeave)||t.call(e)}),A=(0,g.useRender)();return a.default.createElement(v.Provider,{value:S},a.default.createElement(x.Provider,{value:M},A({ourProps:{...R,as:a.Fragment,children:a.default.createElement(j,{ref:p,...R,...i,beforeEnter:P,beforeLeave:L})},theirProps:{},defaultTag:a.Fragment,features:N,visible:"visible"===w,name:"Transition"})))}),j=(0,g.forwardRefWithAs)(function(e,t){var r,s;let{transition:l=!0,beforeEnter:i,afterEnter:b,beforeLeave:k,afterLeave:j,enter:S,enterFrom:T,enterTo:_,entered:E,leave:M,leaveFrom:R,leaveTo:P,...L}=e,[A,I]=(0,a.useState)(null),O=(0,a.useRef)(null),F=f(e),D=(0,d.useSyncRefs)(...F?[O,t,I]:null===t?[]:[t]),H=null==(r=L.unmount)||r?g.RenderStrategy.Unmount:g.RenderStrategy.Hidden,{show:V,appear:B,initial:J}=function(){let e=(0,a.useContext)(x);if(null===e)throw Error("A is used but it is missing a parent or .");return e}(),[U,q]=(0,a.useState)(V?"visible":"hidden"),G=function(){let e=(0,a.useContext)(v);if(null===e)throw Error("A is used but it is missing a parent or .");return e}(),{register:z,unregister:W}=G;(0,o.useIsoMorphicEffect)(()=>z(O),[z,O]),(0,o.useIsoMorphicEffect)(()=>{if(H===g.RenderStrategy.Hidden&&O.current)return V&&"visible"!==U?void q("visible"):(0,p.match)(U,{hidden:()=>W(O),visible:()=>z(O)})},[U,O,z,W,V,H]);let Y=(0,c.useServerHandoffComplete)();(0,o.useIsoMorphicEffect)(()=>{if(F&&Y&&"visible"===U&&null===O.current)throw Error("Did you forget to passthrough the `ref` to the actual DOM node?")},[O,U,Y,F]);let X=J&&!B,Z=B&&V&&J,$=(0,a.useRef)(!1),K=C(()=>{$.current||(q("hidden"),W(O))},G),Q=(0,n.useEvent)(e=>{$.current=!0,K.onStart(O,e?"enter":"leave",e=>{"enter"===e?null==i||i():"leave"===e&&(null==k||k())})}),ee=(0,n.useEvent)(e=>{let t=e?"enter":"leave";$.current=!1,K.onStop(O,t,e=>{"enter"===e?null==b||b():"leave"===e&&(null==j||j())}),"leave"!==t||y(K)||(q("hidden"),W(O))});(0,a.useEffect)(()=>{F&&l||(Q(V),ee(V))},[V,F,l]);let et=!(!l||!F||!Y||X),[,er]=(0,u.useTransition)(et,A,V,{start:Q,end:ee}),ea=(0,g.compact)({ref:D,className:(null==(s=(0,h.classNames)(L.className,Z&&S,Z&&T,er.enter&&S,er.enter&&er.closed&&T,er.enter&&!er.closed&&_,er.leave&&M,er.leave&&!er.closed&&R,er.leave&&er.closed&&P,!er.transition&&V&&E))?void 0:s.trim())||void 0,...(0,u.transitionDataAttributes)(er)}),es=0;"visible"===U&&(es|=m.State.Open),"hidden"===U&&(es|=m.State.Closed),er.enter&&(es|=m.State.Opening),er.leave&&(es|=m.State.Closing);let en=(0,g.useRender)();return a.default.createElement(v.Provider,{value:K},a.default.createElement(m.OpenClosedProvider,{value:es},en({ourProps:ea,theirProps:L,defaultTag:w,features:N,visible:"visible"===U,name:"Transition.Child"})))}),S=(0,g.forwardRefWithAs)(function(e,t){let r=null!==(0,a.useContext)(x),s=null!==(0,m.useOpenClosed)();return a.default.createElement(a.default.Fragment,null,!r&&s?a.default.createElement(k,{ref:t,...e}):a.default.createElement(j,{ref:t,...e}))}),T=Object.assign(k,{Child:S,Root:k});e.s(["Transition",0,T],854056)},206929,e=>{"use strict";var t=e.i(290571),r=e.i(757440),a=e.i(271645),s=e.i(446428),n=e.i(444755),l=e.i(673706),o=e.i(103471),i=e.i(495470),c=e.i(854056),d=e.i(888288);let u=(0,l.makeClassName)("Select"),m=a.default.forwardRef((e,l)=>{let{defaultValue:m="",value:h,onValueChange:p,placeholder:g="Select...",disabled:f=!1,icon:x,enableClear:b=!1,required:v,children:y,name:C,error:w=!1,errorMessage:N,className:k,id:j}=e,S=(0,t.__rest)(e,["defaultValue","value","onValueChange","placeholder","disabled","icon","enableClear","required","children","name","error","errorMessage","className","id"]),T=(0,a.useRef)(null),_=a.Children.toArray(y),[E,M]=(0,d.default)(m,h),R=(0,a.useMemo)(()=>{let e=a.default.Children.toArray(y).filter(a.isValidElement);return(0,o.constructValueToNameMapping)(e)},[y]);return a.default.createElement("div",{className:(0,n.tremorTwMerge)("w-full min-w-[10rem] text-tremor-default",k)},a.default.createElement("div",{className:"relative"},a.default.createElement("select",{title:"select-hidden",required:v,className:(0,n.tremorTwMerge)("h-full w-full absolute left-0 top-0 -z-10 opacity-0"),value:E,onChange:e=>{e.preventDefault()},name:C,disabled:f,id:j,onFocus:()=>{let e=T.current;e&&e.focus()}},a.default.createElement("option",{className:"hidden",value:"",disabled:!0,hidden:!0},g),_.map(e=>{let t=e.props.value,r=e.props.children;return a.default.createElement("option",{className:"hidden",key:t,value:t},r)})),a.default.createElement(i.Listbox,Object.assign({as:"div",ref:l,defaultValue:E,value:E,onChange:e=>{null==p||p(e),M(e)},disabled:f,id:j},S),({value:e})=>{var t;return a.default.createElement(a.default.Fragment,null,a.default.createElement(i.ListboxButton,{ref:T,className:(0,n.tremorTwMerge)("w-full outline-none text-left whitespace-nowrap truncate rounded-tremor-default focus:ring-2 transition duration-100 border pr-8 py-2","border-tremor-border shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:shadow-dark-tremor-input dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",x?"pl-10":"pl-3",(0,o.getSelectButtonColors)((0,o.hasValue)(e),f,w))},x&&a.default.createElement("span",{className:(0,n.tremorTwMerge)("absolute inset-y-0 left-0 flex items-center ml-px pl-2.5")},a.default.createElement(x,{className:(0,n.tremorTwMerge)(u("Icon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),a.default.createElement("span",{className:"w-[90%] block truncate"},e&&null!=(t=R.get(e))?t:g),a.default.createElement("span",{className:(0,n.tremorTwMerge)("absolute inset-y-0 right-0 flex items-center mr-3")},a.default.createElement(r.default,{className:(0,n.tremorTwMerge)(u("arrowDownIcon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}))),b&&E?a.default.createElement("button",{type:"button",className:(0,n.tremorTwMerge)("absolute inset-y-0 right-0 flex items-center mr-8"),onClick:e=>{e.preventDefault(),M(""),null==p||p("")}},a.default.createElement(s.default,{className:(0,n.tremorTwMerge)(u("clearIcon"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null,a.default.createElement(c.Transition,{enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},a.default.createElement(i.ListboxOptions,{anchor:"bottom start",className:(0,n.tremorTwMerge)("z-10 w-[var(--button-width)] divide-y overflow-y-auto outline-none rounded-tremor-default max-h-[228px] border [--anchor-gap:4px]","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},y)))})),w&&N?a.default.createElement("p",{className:(0,n.tremorTwMerge)("errorMessage","text-sm text-rose-500 mt-1")},N):null)});m.displayName="Select",e.s(["Select",0,m],206929)},254709,e=>{"use strict";var t=e.i(843476),r=e.i(584935),a=e.i(304967),s=e.i(309426),n=e.i(350967),l=e.i(752978),o=e.i(621642),i=e.i(25080),c=e.i(37091),d=e.i(197647),u=e.i(653824),m=e.i(881073),h=e.i(404206),p=e.i(723731),g=e.i(599724),f=e.i(271645),x=e.i(727749),b=e.i(144267),v=e.i(278587),y=e.i(602869),C=e.i(994388),w=e.i(220508),N=e.i(964306),k=e.i(551332);let j=({responseTimeMs:e})=>null==e?null:(0,t.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-500 font-mono",children:[(0,t.jsx)("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:(0,t.jsx)("path",{d:"M12 6V12L16 14M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2Z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})}),(0,t.jsxs)("span",{children:[e.toFixed(0),"ms"]})]}),S=e=>{let t=e;if("string"==typeof t)try{t=JSON.parse(t)}catch{}return t},T=({label:e,value:r})=>{let[a,s]=f.default.useState(!1),[n,l]=f.default.useState(!1),o=r?.toString()||"N/A",i=o.length>50?o.substring(0,50)+"...":o;return(0,t.jsx)("tr",{className:"hover:bg-gray-50",children:(0,t.jsx)("td",{className:"px-4 py-2 align-top",colSpan:2,children:(0,t.jsxs)("div",{className:"flex items-center justify-between group",children:[(0,t.jsxs)("div",{className:"flex items-center flex-1",children:[(0,t.jsx)("button",{onClick:()=>s(!a),className:"text-gray-400 hover:text-gray-600 mr-2",children:a?"▼":"▶"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-sm text-gray-600",children:e}),(0,t.jsx)("pre",{className:"mt-1 text-sm font-mono text-gray-800 whitespace-pre-wrap",children:a?o:i})]})]}),(0,t.jsx)("button",{onClick:()=>{navigator.clipboard.writeText(o),l(!0),setTimeout(()=>l(!1),2e3)},className:"opacity-0 group-hover:opacity-100 text-gray-400 hover:text-gray-600",children:(0,t.jsx)(k.ClipboardCopyIcon,{className:"h-4 w-4"})})]})})})},_=({response:e})=>{let r=null,a={},s={};try{if(e?.error)try{let t="string"==typeof e.error.message?JSON.parse(e.error.message):e.error.message;r={message:t?.message||"Unknown error",traceback:t?.traceback||"No traceback available",litellm_params:t?.litellm_cache_params||{},health_check_cache_params:t?.health_check_cache_params||{}},a=S(r.litellm_params)||{},s=S(r.health_check_cache_params)||{}}catch(t){console.warn("Error parsing error details:",t),r={message:String(e.error.message||"Unknown error"),traceback:"Error parsing details",litellm_params:{},health_check_cache_params:{}}}else a=S(e?.litellm_cache_params)||{},s=S(e?.health_check_cache_params)||{}}catch(e){console.warn("Error in response parsing:",e),a={},s={}}let n={redis_host:s?.redis_client?.connection_pool?.connection_kwargs?.host||s?.redis_async_client?.connection_pool?.connection_kwargs?.host||s?.connection_kwargs?.host||s?.host||"N/A",redis_port:s?.redis_client?.connection_pool?.connection_kwargs?.port||s?.redis_async_client?.connection_pool?.connection_kwargs?.port||s?.connection_kwargs?.port||s?.port||"N/A",redis_version:s?.redis_version||"N/A",startup_nodes:(()=>{try{if(s?.redis_kwargs?.startup_nodes)return JSON.stringify(s.redis_kwargs.startup_nodes);let e=s?.redis_client?.connection_pool?.connection_kwargs?.host||s?.redis_async_client?.connection_pool?.connection_kwargs?.host,t=s?.redis_client?.connection_pool?.connection_kwargs?.port||s?.redis_async_client?.connection_pool?.connection_kwargs?.port;return e&&t?JSON.stringify([{host:e,port:t}]):"N/A"}catch(e){return"N/A"}})(),namespace:s?.namespace||"N/A"};return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm",children:(0,t.jsxs)(u.TabGroup,{children:[(0,t.jsxs)(m.TabList,{className:"border-b border-gray-200 px-4",children:[(0,t.jsx)(d.Tab,{className:"px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800",children:"Summary"}),(0,t.jsx)(d.Tab,{className:"px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800",children:"Raw Response"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(h.TabPanel,{className:"p-4",children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex items-center mb-6",children:[e?.status==="healthy"?(0,t.jsx)(w.CheckCircleIcon,{className:"h-5 w-5 text-green-500 mr-2"}):(0,t.jsx)(N.XCircleIcon,{className:"h-5 w-5 text-red-500 mr-2"}),(0,t.jsxs)(g.Text,{className:`text-sm font-medium ${e?.status==="healthy"?"text-green-500":"text-red-500"}`,children:["Cache Status: ",e?.status||"unhealthy"]})]}),(0,t.jsx)("table",{className:"w-full border-collapse",children:(0,t.jsxs)("tbody",{children:[r&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("tr",{children:(0,t.jsx)("td",{colSpan:2,className:"pt-4 pb-2 font-semibold text-red-600",children:"Error Details"})}),(0,t.jsx)(T,{label:"Error Message",value:r.message}),(0,t.jsx)(T,{label:"Traceback",value:r.traceback})]}),(0,t.jsx)("tr",{children:(0,t.jsx)("td",{colSpan:2,className:"pt-4 pb-2 font-semibold",children:"Cache Details"})}),(0,t.jsx)(T,{label:"Cache Configuration",value:String(a?.type)}),(0,t.jsx)(T,{label:"Ping Response",value:String(e.ping_response)}),(0,t.jsx)(T,{label:"Set Cache Response",value:e.set_cache_response||"N/A"}),(0,t.jsx)(T,{label:"litellm_settings.cache_params",value:JSON.stringify(a,null,2)}),a?.type==="redis"&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("tr",{children:(0,t.jsx)("td",{colSpan:2,className:"pt-4 pb-2 font-semibold",children:"Redis Details"})}),(0,t.jsx)(T,{label:"Redis Host",value:n.redis_host||"N/A"}),(0,t.jsx)(T,{label:"Redis Port",value:n.redis_port||"N/A"}),(0,t.jsx)(T,{label:"Redis Version",value:n.redis_version||"N/A"}),(0,t.jsx)(T,{label:"Startup Nodes",value:n.startup_nodes||"N/A"}),(0,t.jsx)(T,{label:"Namespace",value:n.namespace||"N/A"})]})]})})]})}),(0,t.jsx)(h.TabPanel,{className:"p-4",children:(0,t.jsx)("div",{className:"bg-gray-50 rounded-md p-4 font-mono text-sm",children:(0,t.jsx)("pre",{className:"whitespace-pre-wrap wrap-break-word overflow-auto max-h-[500px]",children:(()=>{try{let t={...e,litellm_cache_params:a,health_check_cache_params:s},r=JSON.parse(JSON.stringify(t,(e,t)=>{if("string"==typeof t)try{return JSON.parse(t)}catch{}return t}));return JSON.stringify(r,null,2)}catch(e){return"Error formatting JSON: "+e.message}})()})})})]})]})})},E=({accessToken:e,healthCheckResponse:r,runCachingHealthCheck:a,responseTimeMs:s})=>{let[n,l]=f.default.useState(null),[o,i]=f.default.useState(!1),c=async()=>{i(!0);let e=performance.now();await a(),l(performance.now()-e),i(!1)};return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)(C.Button,{onClick:c,disabled:o,className:"bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-400 text-white text-sm px-4 py-2 rounded-md",children:o?"Running Health Check...":"Run Health Check"}),(0,t.jsx)(j,{responseTimeMs:n})]}),r&&(0,t.jsx)(_,{response:r})]})};var M=e.i(677667),R=e.i(898667),P=e.i(130643),L=e.i(808613),A=e.i(695411),I=e.i(206929),O=e.i(35983);let F=({redisType:e,redisTypeDescriptions:r,onTypeChange:a})=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Redis Type"}),(0,t.jsxs)(I.Select,{value:e,onValueChange:a,children:[(0,t.jsx)(O.SelectItem,{value:"node",children:"Node (Single Instance)"}),(0,t.jsx)(O.SelectItem,{value:"cluster",children:"Cluster"}),(0,t.jsx)(O.SelectItem,{value:"sentinel",children:"Sentinel"}),(0,t.jsx)(O.SelectItem,{value:"semantic",children:"Semantic"})]}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:r[e]||"Select the type of Redis deployment you're using"})]});var D=e.i(311451),H=e.i(199133),V=e.i(790848);let B=({field:e,embeddingModels:r})=>(0,t.jsx)(L.Form.Item,{name:e.name,label:e.label,extra:e.helpText,rules:e.rules,valuePropName:"boolean"===e.type?"checked":"value",children:((e,r)=>{switch(e.type){case"boolean":return(0,t.jsx)(V.Switch,{});case"password":return(0,t.jsx)(D.Input.Password,{placeholder:e.helpText,autoComplete:"new-password"});case"integer":case"float":return(0,t.jsx)(D.Input,{inputMode:"decimal",placeholder:e.helpText});case"list":return(0,t.jsx)(D.Input.TextArea,{rows:4,placeholder:e.helpText});case"model-select":return(0,t.jsx)(H.Select,{showSearch:!0,allowClear:!0,placeholder:"Search and select a model...",options:r,optionFilterProp:"label",style:{width:"100%"}});default:return(0,t.jsx)(D.Input,{placeholder:e.helpText})}})(e,r)}),J=["node","cluster","sentinel","semantic"],U={node:"Standard Redis node/single instance",cluster:"Redis Cluster mode for high availability and horizontal scaling",sentinel:"Redis Sentinel mode for high availability with automatic failover",semantic:"Semantic caching that reuses responses for similar prompts"},q={validator:(e,t)=>{let r;if(null==t||""===String(t).trim())return Promise.resolve();try{r=JSON.parse(String(t))}catch{return Promise.reject(Error("Must be a valid JSON array (use double quotes)"))}return Array.isArray(r)?Promise.resolve():Promise.reject(Error("Must be a JSON array"))}},G={validator:(e,t)=>{if(null==t||""===String(t).trim())return Promise.resolve();let r=Number(t);return!Number.isInteger(r)||r<0?Promise.reject(Error("Must be a non-negative integer")):Promise.resolve()}},z={validator:(e,t)=>null==t||""===String(t).trim()?Promise.resolve():Number.isNaN(Number(t))?Promise.reject(Error("Must be a number")):Promise.resolve()},W=[{name:"url",label:"Redis URL",type:"string",section:"connection",helpText:"Full Redis/Valkey connection URL (e.g. redis://:password@host:6379/1). When set, it takes precedence over Host, Port, Password, and Database Index.",redisType:null},{name:"host",label:"Host",type:"string",section:"connection",helpText:"Redis server hostname or IP address",redisType:null},{name:"port",label:"Port",type:"string",section:"connection",helpText:"Redis server port number",redisType:null,defaultValue:"6379",rules:[{validator:(e,t)=>{if(null==t||""===String(t).trim())return Promise.resolve();let r=Number(t);return!Number.isInteger(r)||r<1||r>65535?Promise.reject(Error("Port must be an integer between 1 and 65535")):Promise.resolve()}}]},{name:"db",label:"Database Index",type:"integer",section:"connection",helpText:"Logical database index to isolate the cache (e.g. 1 for redis://host:6379/1)",redisType:null,rules:[G]},{name:"password",label:"Password",type:"password",section:"connection",helpText:"Redis server password",redisType:null},{name:"username",label:"Username",type:"string",section:"connection",helpText:"Redis server username (if required)",redisType:null},{name:"redis_startup_nodes",label:"Startup Nodes",type:"list",section:"cluster",helpText:'List of startup nodes for Redis Cluster (e.g., [{"host": "127.0.0.1", "port": "7001"}])',redisType:"cluster",rules:[q]},{name:"sentinel_nodes",label:"Sentinel Nodes",type:"list",section:"sentinel",helpText:'List of Sentinel nodes (e.g., [["localhost", 26379]])',redisType:"sentinel",rules:[q]},{name:"service_name",label:"Service Name",type:"string",section:"sentinel",helpText:"Master service name for Redis Sentinel",redisType:"sentinel"},{name:"sentinel_password",label:"Sentinel Password",type:"password",section:"sentinel",helpText:"Password for Redis Sentinel authentication",redisType:"sentinel"},{name:"similarity_threshold",label:"Similarity Threshold",type:"float",section:"semantic",helpText:"Similarity threshold for semantic cache",redisType:"semantic",defaultValue:.8,rules:[z]},{name:"redis_semantic_cache_embedding_model",label:"Embedding Model",type:"model-select",section:"semantic",helpText:"Embedding model for semantic cache",redisType:"semantic"},{name:"ssl",label:"SSL",type:"boolean",section:"ssl",helpText:"Enable SSL/TLS connection",redisType:null,defaultValue:!1},{name:"ssl_cert_reqs",label:"SSL Cert Reqs",type:"string",section:"ssl",helpText:"SSL certificate requirements (None, CERT_REQUIRED, CERT_OPTIONAL)",redisType:null},{name:"ssl_check_hostname",label:"SSL Check Hostname",type:"boolean",section:"ssl",helpText:"Enable SSL hostname verification",redisType:null,defaultValue:!1},{name:"namespace",label:"Namespace",type:"string",section:"cacheManagement",helpText:"Namespace prefix for cache keys",redisType:null},{name:"ttl",label:"TTL (seconds)",type:"float",section:"cacheManagement",helpText:"Time-to-live for cached items in seconds",redisType:null,rules:[z]},{name:"max_connections",label:"Max Connections",type:"integer",section:"cacheManagement",helpText:"Maximum number of connections in the connection pool",redisType:null,rules:[G]},{name:"gcp_service_account",label:"GCP Service Account",type:"string",section:"gcp",helpText:"GCP service account for IAM authentication (e.g., projects/-/serviceAccounts/your-sa@project.iam.gserviceaccount.com)",redisType:null},{name:"gcp_ssl_ca_certs",label:"GCP SSL CA Certs",type:"string",section:"gcp",helpText:"Path to SSL CA certificate file for GCP Memorystore Redis",redisType:null}],Y=(e,t)=>null===e.redisType||e.redisType===t,X=(e,t,{forTesting:r})=>({type:r||"semantic"!==e?"redis":"redis-semantic",...Object.fromEntries(W.filter(t=>Y(t,e)).flatMap(e=>{let r=((e,t)=>{if("boolean"===e.type)return!!t;if("list"===e.type){if("string"!=typeof t||""===t.trim())return;try{return JSON.parse(t)}catch{return}}if("integer"===e.type||"float"===e.type){if(null==t||""===t)return;let e=Number(t);return Number.isNaN(e)?void 0:e}if("string"!=typeof t)return void 0===t?void 0:String(t);let r=t.trim();return""===r?void 0:r})(e,t[e.name]);return void 0===r?[]:[[e.name,r]]}))}),Z=({title:e,section:r,redisType:a,embeddingModels:s,gridCols:n="grid-cols-1 gap-6 sm:grid-cols-2",headingLevel:l="h4"})=>{let o=W.filter(e=>e.section===r&&Y(e,a));return 0===o.length?null:(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)(l,{className:"text-sm font-medium text-gray-900",children:e}),(0,t.jsx)("div",{className:`grid ${n}`,children:o.map(e=>(0,t.jsx)(B,{field:e,embeddingModels:s},e.name))})]})},$=e=>J.includes(e)?e:"node",K=({accessToken:e})=>{let[r]=L.Form.useForm(),[a,s]=(0,f.useState)("node"),[n,l]=(0,f.useState)([]),[o,i]=(0,f.useState)(!1),[c,d]=(0,f.useState)(!1),u=(0,f.useCallback)(async()=>{if(e)try{let t=(await (0,y.getCacheSettingsCall)(e)).current_values??{};r.setFieldsValue(Object.fromEntries(W.map(e=>{let r;return[e.name,(r=t[e.name]??e.defaultValue,"boolean"===e.type?!0===r||"true"===r:"list"===e.type?null==r||""===r?"":"string"==typeof r?r:JSON.stringify(r,null,2):null==r?"":String(r))]}))),s($(t.redis_type))}catch(e){console.error("Failed to load cache settings:",e),x.default.fromBackend("Failed to load cache settings")}},[e,r]);(0,f.useEffect)(()=>{u()},[u]),(0,f.useEffect)(()=>{e&&(0,A.fetchAvailableModels)(e).then(e=>l(e.filter(e=>"embedding"===e.mode).map(e=>({value:e.model_group,label:e.model_group})))).catch(e=>console.error("Error fetching embedding models:",e))},[e]);let m=async()=>{try{return await r.validateFields()}catch{return null}},h=async()=>{if(!e)return;let t=await m();if(null!==t){i(!0);try{let r=await (0,y.testCacheConnectionCall)(e,X(a,t,{forTesting:!0}));"success"===r.status?x.default.success("Cache connection test successful!"):x.default.fromBackend(`Connection test failed: ${r.message||r.error}`)}catch(e){console.error("Test connection error:",e),x.default.fromBackend(`Connection test failed: ${e instanceof Error?e.message:"Unknown error"}`)}finally{i(!1)}}},p=async()=>{if(!e)return;let t=await m();if(null!==t){d(!0);try{await (0,y.updateCacheSettingsCall)(e,X(a,t,{forTesting:!1})),x.default.success("Cache settings updated successfully"),await u()}catch(e){console.error("Failed to save cache settings:",e),x.default.fromBackend("Failed to update cache settings")}finally{d(!1)}}};return e?(0,t.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,t.jsxs)(L.Form,{form:r,layout:"vertical",requiredMark:!1,className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Cache Settings"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure Redis cache for LiteLLM"})]}),(0,t.jsx)(F,{redisType:a,redisTypeDescriptions:U,onTypeChange:e=>s($(e))}),(0,t.jsx)("div",{className:"pt-4 border-t border-gray-200",children:(0,t.jsx)(Z,{title:"Connection Settings",section:"connection",redisType:a,embeddingModels:n})}),"cluster"===a&&(0,t.jsx)("div",{className:"pt-4 border-t border-gray-200",children:(0,t.jsx)(Z,{title:"Cluster Configuration",section:"cluster",redisType:a,embeddingModels:n,gridCols:"grid-cols-1 gap-6"})}),"sentinel"===a&&(0,t.jsx)("div",{className:"pt-4 border-t border-gray-200",children:(0,t.jsx)(Z,{title:"Sentinel Configuration",section:"sentinel",redisType:a,embeddingModels:n})}),"semantic"===a&&(0,t.jsx)("div",{className:"pt-4 border-t border-gray-200",children:(0,t.jsx)(Z,{title:"Semantic Configuration",section:"semantic",redisType:a,embeddingModels:n})}),(0,t.jsxs)(M.Accordion,{className:"mt-4",children:[(0,t.jsx)(R.AccordionHeader,{children:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900",children:"Advanced Settings"})}),(0,t.jsx)(P.AccordionBody,{children:(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)(Z,{title:"SSL Settings",section:"ssl",redisType:a,embeddingModels:n,headingLevel:"h5"}),(0,t.jsx)(Z,{title:"Cache Management",section:"cacheManagement",redisType:a,embeddingModels:n,headingLevel:"h5"}),(0,t.jsx)(Z,{title:"GCP Authentication",section:"gcp",redisType:a,embeddingModels:n,headingLevel:"h5"})]})})]})]}),(0,t.jsxs)("div",{className:"border-t border-gray-200 pt-6 flex justify-end gap-3",children:[(0,t.jsx)(C.Button,{variant:"secondary",size:"sm",onClick:h,disabled:o,className:"text-sm",children:o?"Testing...":"Test Connection"}),(0,t.jsx)(C.Button,{size:"sm",onClick:p,disabled:c,className:"text-sm font-medium",children:c?"Saving...":"Save Changes"})]})]}):null},Q=e=>{if(e)return e.toISOString().split("T")[0]};function ee(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}let et=({accessToken:e,token:C,userRole:w,userID:N,premiumUser:k})=>{let[j,S]=(0,f.useState)([]),[T,_]=(0,f.useState)([]),[M,R]=(0,f.useState)([]),[P,L]=(0,f.useState)([]),[A,I]=(0,f.useState)("0"),[O,F]=(0,f.useState)("0"),[D,H]=(0,f.useState)("0"),[V,B]=(0,f.useState)({from:new Date(Date.now()-6048e5),to:new Date}),[J,U]=(0,f.useState)(""),[q,G]=(0,f.useState)("");(0,f.useEffect)(()=>{e&&V&&((async()=>{L(await (0,y.adminGlobalCacheActivity)(e,Q(V.from),Q(V.to)))})(),U(new Date().toLocaleString()))},[e]);let z=Array.from(new Set(P.map(e=>e?.api_key??""))),W=Array.from(new Set(P.map(e=>e?.model??"")));Array.from(new Set(P.map(e=>e?.call_type??"")));let Y=async(t,r)=>{t&&r&&e&&L(await (0,y.adminGlobalCacheActivity)(e,Q(t),Q(r)))};(0,f.useEffect)(()=>{let e=P;T.length>0&&(e=e.filter(e=>T.includes(e.api_key))),M.length>0&&(e=e.filter(e=>M.includes(e.model)));let t=0,r=0,a=0,s=e.reduce((e,s)=>{s.call_type||(s.call_type="Unknown"),t+=(s.total_rows||0)-(s.cache_hit_true_rows||0),r+=s.cache_hit_true_rows||0,a+=s.cached_completion_tokens||0;let n=e.find(e=>e.name===s.call_type);return n?(n["LLM API requests"]+=(s.total_rows||0)-(s.cache_hit_true_rows||0),n["Cache hit"]+=s.cache_hit_true_rows||0,n["Cached Completion Tokens"]+=s.cached_completion_tokens||0,n["Generated Completion Tokens"]+=s.generated_completion_tokens||0):e.push({name:s.call_type,"LLM API requests":(s.total_rows||0)-(s.cache_hit_true_rows||0),"Cache hit":s.cache_hit_true_rows||0,"Cached Completion Tokens":s.cached_completion_tokens||0,"Generated Completion Tokens":s.generated_completion_tokens||0}),e},[]);I(ee(r)),F(ee(a));let n=r+t;n>0?H((r/n*100).toFixed(2)):H("0"),S(s)},[T,M,V,P]);let X=async()=>{try{x.default.info("Running cache health check..."),G("");let t=await (0,y.cachingHealthCheckCall)(null!==e?e:"");G(t)}catch(t){let e;if(console.error("Error running health check:",t),t&&t.message)try{let r=JSON.parse(t.message);r.error&&(r=r.error),e=r}catch(r){e={message:t.message}}else e={message:"Unknown error occurred"};G({error:e})}};return(0,t.jsxs)(u.TabGroup,{className:"gap-2 p-8 h-full w-full mt-2 mb-8",children:[(0,t.jsxs)(m.TabList,{className:"flex justify-between mt-2 w-full items-center",children:[(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)(d.Tab,{children:"Cache Analytics"}),(0,t.jsx)(d.Tab,{children:"Cache Health"}),(0,t.jsx)(d.Tab,{children:"Cache Settings"})]}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[J&&(0,t.jsxs)(g.Text,{children:["Last Refreshed: ",J]}),(0,t.jsx)(l.Icon,{icon:v.RefreshIcon,variant:"shadow",size:"xs",className:"self-center",onClick:()=>{U(new Date().toLocaleString())}})]})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(a.Card,{children:[(0,t.jsxs)(n.Grid,{numItems:3,className:"gap-4 mt-4",children:[(0,t.jsx)(s.Col,{children:(0,t.jsx)(o.MultiSelect,{placeholder:"Select Virtual Keys",value:T,onValueChange:_,children:z.map(e=>(0,t.jsx)(i.MultiSelectItem,{value:e,children:e},e))})}),(0,t.jsx)(s.Col,{children:(0,t.jsx)(o.MultiSelect,{placeholder:"Select Models",value:M,onValueChange:R,children:W.map(e=>(0,t.jsx)(i.MultiSelectItem,{value:e,children:e},e))})}),(0,t.jsx)(s.Col,{children:(0,t.jsx)(b.default,{value:V,onValueChange:e=>{B(e),Y(e.from,e.to)}})})]}),(0,t.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-4",children:[(0,t.jsxs)(a.Card,{children:[(0,t.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hit Ratio"}),(0,t.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,t.jsxs)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:[D,"%"]})})]}),(0,t.jsxs)(a.Card,{children:[(0,t.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hits"}),(0,t.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,t.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:A})})]}),(0,t.jsxs)(a.Card,{children:[(0,t.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cached Tokens"}),(0,t.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,t.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:O})})]})]}),(0,t.jsx)(c.Subtitle,{className:"mt-4",children:"Cache Hits vs API Requests"}),(0,t.jsx)(r.BarChart,{title:"Cache Hits vs API Requests",data:j,stack:!0,index:"name",valueFormatter:ee,categories:["LLM API requests","Cache hit"],colors:["sky","teal"],yAxisWidth:48}),(0,t.jsx)(c.Subtitle,{className:"mt-4",children:"Cached Completion Tokens vs Generated Completion Tokens"}),(0,t.jsx)(r.BarChart,{className:"mt-6",data:j,stack:!0,index:"name",valueFormatter:ee,categories:["Generated Completion Tokens","Cached Completion Tokens"],colors:["sky","teal"],yAxisWidth:48})]})}),(0,t.jsx)(h.TabPanel,{children:(0,t.jsx)(E,{accessToken:e,healthCheckResponse:q,runCachingHealthCheck:X})}),(0,t.jsx)(h.TabPanel,{children:(0,t.jsx)(K,{accessToken:e,userRole:w,userID:N})})]})]})};var er=e.i(135214);e.s(["default",0,function(){let{accessToken:e,userRole:r,userId:a,token:s,premiumUser:n}=(0,er.default)();return(0,t.jsx)(et,{userID:a,userRole:r,token:s,accessToken:e,premiumUser:n})}],254709)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/00pr1xqcusy8a.js b/litellm/proxy/_experimental/out/_next/static/chunks/00pr1xqcusy8a.js deleted file mode 100644 index a6f74b19695..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/00pr1xqcusy8a.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,207082,e=>{"use strict";var t=e.i(619273),s=e.i(266027),l=e.i(243652),a=e.i(602869),r=e.i(431703),i=e.i(135214);let n=(0,l.createQueryKeys)("keys"),o=async(e,t,s,l={})=>{try{let i=(0,a.getProxyBaseUrl)(),n=new URLSearchParams(Object.entries({team_id:l.teamID,project_id:l.projectID,agent_id:l.agentID,organization_id:l.organizationID,key_alias:l.selectedKeyAlias,key_hash:l.keyHash,user_id:l.userID,page:t,size:s,sort_by:l.sortBy,sort_order:l.sortOrder,expand:l.expand,status:l.status,return_full_object:"true",include_team_keys:"true",include_created_by_keys:"true",substring_matching:"true"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),o=`${i?`${i}/key/list`:"/key/list"}?${n}`,d=await fetch(o,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!d.ok){let e=await d.json(),t=(0,r.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return await d.json()}catch(e){throw console.error("Failed to list keys:",e),e}},d=(0,l.createQueryKeys)("deletedKeys");e.s(["keyKeys",0,n,"useDeletedKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:d.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,{...a,status:"deleted"}),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})},"useKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:n.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,a),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})}])},510674,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(431703),r=e.i(135214),i=e.i(708347);let n=(0,s.createQueryKeys)("projects"),o=[...i.all_admin_roles,...i.internalUserRoles],d=async e=>{let t=(0,l.getProxyBaseUrl)(),s=`${t}/project/list`,r=await fetch(s,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,a.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return r.json()};e.s(["projectKeys",0,n,"useProjects",0,()=>{let{accessToken:e,userRole:s}=(0,r.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>d(e),enabled:!!e&&o.includes(s)})}])},557662,e=>{"use strict";let t="/ui/assets/logos/",s=[{id:"arize",displayName:"Arize",logo:`${t}arize.png`,supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_id:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:`${t}braintrust.png`,supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:`${t}custom.svg`,supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"galileo",displayName:"Galileo",logo:`${t}galileo.ico`,supports_key_team_logging:!1,dynamic_params:{GALILEO_API_KEY:"password",GALILEO_PROJECT_ID:"text",GALILEO_LOG_STREAM_ID:"text",GALILEO_BASE_URL:"text",GALILEO_USERNAME:"text",GALILEO_PASSWORD:"password"},description:"Galileo AI Observability Integration"},{id:"datadog",displayName:"Datadog",logo:`${t}datadog.png`,supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:`${t}lago.svg`,supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:`${t}langsmith.png`,supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:`${t}openmeter.png`,supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:`${t}otel.png`,supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"},{id:"SQS",displayName:"SQS",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{sqs_queue_url:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"SQS Queue (AWS) Logging Integration"}],l=s.reduce((e,t)=>(e[t.displayName]=t,e),{}),a=s.reduce((e,t)=>(e[t.displayName]=t.id,e),{}),r=s.reduce((e,t)=>(e[t.id]=t.displayName,e),{});e.s(["callbackInfo",0,l,"callback_map",0,a,"mapDisplayToInternalNames",0,e=>e.map(e=>a[e]||e),"mapInternalToDisplayNames",0,e=>e.map(e=>r[e]||e),"reverse_callback_map",0,r])},810757,477386,e=>{"use strict";var t=e.i(271645);let s=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}))});e.s(["CogIcon",0,s],810757);let l=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"}))});e.s(["BanIcon",0,l],477386)},552130,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select agents",disabled:d=!1})=>{let[c,u]=(0,s.useState)([]),[m,p]=(0,s.useState)([]),[g,h]=(0,s.useState)(!1);(0,s.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,a.getAgentsList)(n),t=e?.agents||[];u(t);let s=new Set;t.forEach(e=>{let t=e.agent_access_groups;t&&Array.isArray(t)&&t.forEach(e=>s.add(e))}),p(Array.from(s))}catch(e){console.error("Error fetching agents:",e)}finally{h(!1)}}})()},[n]);let x=[...m.map(e=>({label:e,value:`group:${e}`,isAccessGroup:!0,searchText:`${e} Access Group`})),...c.map(e=>({label:`${e.agent_name||e.agent_id}`,value:e.agent_id,isAccessGroup:!1,searchText:`${e.agent_name||e.agent_id} ${e.agent_id} Agent`}))],y=[...r?.agents||[],...(r?.accessGroups||[]).map(e=>`group:${e}`)];return(0,t.jsx)("div",{children:(0,t.jsx)(l.Select,{mode:"multiple",placeholder:o,onChange:t=>{e({agents:t.filter(e=>!e.startsWith("group:")),accessGroups:t.filter(e=>e.startsWith("group:")).map(e=>e.replace("group:",""))})},value:y,loading:g,className:i,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:d,filterOption:(e,t)=>(x.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:x.map(e=>(0,t.jsx)(l.Select.Option,{value:e.value,label:e.label,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:e.isAccessGroup?"#52c41a":"#722ed1",flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:e.isAccessGroup?"#52c41a":"#722ed1",fontSize:"12px",fontWeight:500,opacity:.8},children:e.isAccessGroup?"Access Group":"Agent"})]})},e.value))})})}])},9314,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(981339),a=e.i(645526),r=e.i(599724),i=e.i(263147);e.s(["default",0,({value:e,onChange:n,placeholder:o="Select access groups",disabled:d=!1,style:c,className:u,showLabel:m=!1,labelText:p="Access Group",allowClear:g=!0})=>{let{data:h,isLoading:x,isError:y}=(0,i.useAccessGroups)();if(x)return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(l.Skeleton.Input,{active:!0,block:!0,style:{height:32,...c}})]});let f=(h??[]).map(e=>({label:(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"font-medium",children:e.access_group_name})," ",(0,t.jsxs)("span",{className:"text-gray-400 text-xs",children:["(",e.access_group_id,")"]})]}),value:e.access_group_id,selectedLabel:e.access_group_name,searchText:`${e.access_group_name} ${e.access_group_id}`}));return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(s.Select,{mode:"multiple",value:e,placeholder:o,onChange:n,disabled:d,allowClear:g,showSearch:!0,style:{width:"100%",...c},className:`rounded-md ${u??""}`,notFoundContent:y?(0,t.jsx)("span",{className:"text-red-500",children:"Failed to load access groups"}):"No access groups found",filterOption:(e,t)=>(f.find(e=>e.value===t?.value)?.searchText??"").toLowerCase().includes(e.toLowerCase()),optionLabelProp:"selectedLabel",options:f.map(e=>({label:e.label,value:e.value,selectedLabel:e.selectedLabel}))})]})}])},392110,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(592968),r=e.i(312361),i=e.i(790848),n=e.i(536916),o=e.i(827252),d=e.i(779241);let{Option:c}=l.Select;e.s(["default",0,({form:e,autoRotationEnabled:u,onAutoRotationChange:m,rotationInterval:p,onRotationIntervalChange:g,isCreateMode:h=!1,neverExpire:x=!1,onNeverExpireChange:y})=>{let f=p&&!["7d","30d","90d","180d","365d"].includes(p),[b,_]=(0,s.useState)(f),[j,v]=(0,s.useState)(f?p:""),[w,N]=(0,s.useState)(e?.getFieldValue?.("duration")||"");return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Key Expiry Settings"}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Expire Key"}),(0,t.jsx)(a.Tooltip,{title:"Set when this key should expire. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days). Leave empty to keep the current expiry unchanged.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),!h&&y&&(0,t.jsx)(n.Checkbox,{checked:x,onChange:t=>{let s=t.target.checked;y(s),s&&(N(""),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",""):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:""}))},className:"ml-2 text-sm font-normal text-gray-600",children:"Never Expire"})]}),(0,t.jsx)(d.TextInput,{name:"duration",placeholder:h?"e.g., 30d or leave empty to never expire":"e.g., 30d",className:"w-full",value:w,onValueChange:t=>{N(t),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",t):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:t})},disabled:!h&&x})]})]}),(0,t.jsx)(r.Divider,{}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Auto-Rotation Settings"}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Enable Auto-Rotation"}),(0,t.jsx)(a.Tooltip,{title:"Key will automatically regenerate at the specified interval for enhanced security.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsx)(i.Switch,{checked:u,onChange:m,size:"default",className:u?"":"bg-gray-400"})]}),u&&(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Rotation Interval"}),(0,t.jsx)(a.Tooltip,{title:"How often the key should be automatically rotated. Choose the interval that best fits your security requirements.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)(l.Select,{value:b?"custom":p,onChange:e=>{"custom"===e?_(!0):(_(!1),v(""),g(e))},className:"w-full",placeholder:"Select interval",children:[(0,t.jsx)(c,{value:"7d",children:"7 days"}),(0,t.jsx)(c,{value:"30d",children:"30 days"}),(0,t.jsx)(c,{value:"90d",children:"90 days"}),(0,t.jsx)(c,{value:"180d",children:"180 days"}),(0,t.jsx)(c,{value:"365d",children:"365 days"}),(0,t.jsx)(c,{value:"custom",children:"Custom interval"})]}),b&&(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(d.TextInput,{value:j,onChange:e=>{let t=e.target.value;v(t),g(t)},placeholder:"e.g., 1s, 5m, 2h, 14d"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Supported formats: seconds (s), minutes (m), hours (h), days (d)"})]})]})]})]}),u&&(0,t.jsx)("div",{className:"bg-blue-50 p-3 rounded-md text-sm text-blue-700",children:"When rotation occurs, you'll receive a notification with the new key. The old key will be deactivated after a brief grace period."})]})]})}])},844565,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select pass through routes",disabled:d=!1,teamId:c})=>{let[u,m]=(0,s.useState)([]),[p,g]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.getPassThroughEndpointsCall)(n,c);if(e.endpoints){let t=e.endpoints.flatMap(e=>{let t=e.path,s=e.methods;return s&&s.length>0?s.map(e=>({label:`${e} ${t}`,value:t})):[{label:t,value:t}]});m(t)}}catch(e){console.error("Error fetching pass through routes:",e)}finally{g(!1)}}})()},[n,c]),(0,t.jsx)(l.Select,{mode:"tags",placeholder:o,onChange:e,value:r,loading:p,className:i,allowClear:!0,options:u,optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})}])},939510,e=>{"use strict";var t=e.i(843476),s=e.i(808613),l=e.i(199133),a=e.i(592968),r=e.i(827252);let{Option:i}=l.Select;e.s(["default",0,({type:e,name:n,showDetailedDescriptions:o=!0,className:d="",initialValue:c=null,form:u,onChange:m})=>{let p=e.toUpperCase(),g=e.toLowerCase(),h=`Select 'guaranteed_throughput' to prevent overallocating ${p} limit when the key belongs to a Team with specific ${p} limits.`;return(0,t.jsx)(s.Form.Item,{label:(0,t.jsxs)("span",{children:[p," Rate Limit Type"," ",(0,t.jsx)(a.Tooltip,{title:h,children:(0,t.jsx)(r.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:n,initialValue:c,className:d,children:(0,t.jsx)(l.Select,{defaultValue:o?"default":void 0,placeholder:"Select rate limit type",style:{width:"100%"},optionLabelProp:o?"label":void 0,onChange:e=>{u&&u.setFieldValue(n,e),m&&m(e)},children:o?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",label:"Default",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Default"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Best effort throughput - no error if we're overallocating ",g," (Team/Key Limits checked at runtime)."]})]})}),(0,t.jsx)(i,{value:"guaranteed_throughput",label:"Guaranteed throughput",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Guaranteed throughput"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Guaranteed throughput - raise an error if we're overallocating ",g," (also checks model-specific limits)"]})]})}),(0,t.jsx)(i,{value:"dynamic",label:"Dynamic",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Dynamic"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["If the key has a set ",p," (e.g. 2 ",p,") and there are no 429 errors, it can dynamically exceed the limit when the model being called is not erroring."]})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",children:"Best effort throughput"}),(0,t.jsx)(i,{value:"guaranteed_throughput",children:"Guaranteed throughput"}),(0,t.jsx)(i,{value:"dynamic",children:"Dynamic"})]})})})}])},363256,e=>{"use strict";var t=e.i(843476),s=e.i(199133);let{Text:l}=e.i(898586).Typography;e.s(["default",0,({organizations:e,value:a,onChange:r,disabled:i,loading:n,style:o})=>(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"All Organizations",value:a,onChange:r,disabled:i,loading:n,allowClear:!0,style:{minWidth:280,...o},filterOption:(t,s)=>{if(!s)return!1;let l=e?.find(e=>e.organization_id===s.key);if(!l)return!1;let a=t.toLowerCase().trim(),r=(l.organization_alias||"").toLowerCase(),i=(l.organization_id||"").toLowerCase();return r.includes(a)||i.includes(a)},children:e?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.organization_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.organization_alias})," ",(0,t.jsxs)(l,{type:"secondary",children:["(",e.organization_id,")"]})]},e.organization_id))})])},128233,319312,e=>{"use strict";var t=e.i(843476),s=e.i(464571),l=e.i(199133),a=e.i(592968),r=e.i(425063),i=e.i(107233),n=e.i(37727),o=e.i(271645);e.s(["BudgetFallbacksEditor",0,function({value:e,onChange:d,availableModels:c}){let[u,m]=(0,o.useState)(()=>{let t;return 0===(t=Object.keys(e)).length?[]:t.map((t,s)=>({id:String(s+1),primaryModel:t,fallbackModels:e[t]}))}),p=e=>{m(e),d(Object.fromEntries(e.filter(e=>null!==e.primaryModel&&e.fallbackModels.length>0).map(e=>[e.primaryModel,e.fallbackModels])))},g=()=>{p([...u,{id:Date.now().toString(),primaryModel:null,fallbackModels:[]}])},h=(e,t)=>{p(u.map(s=>s.id===e?{...s,...t}:s))},x=new Set(u.map(e=>e.primaryModel).filter(Boolean));return 0===u.length?(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-2",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),u.map(e=>{let s=c.filter(t=>t===e.primaryModel||!x.has(t)),i=c.filter(t=>t!==e.primaryModel);return(0,t.jsxs)("div",{className:"relative rounded-lg border border-gray-200 bg-gray-50 p-4",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{var t;return t=e.id,void p(u.filter(e=>e.id!==t))},className:"absolute top-2 right-2 text-gray-400 hover:text-red-500 transition-colors p-1",children:(0,t.jsx)(n.X,{className:"w-4 h-4"})}),(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Primary Model"}),(0,t.jsx)(l.Select,{className:"w-full",placeholder:"Select model",value:e.primaryModel,onChange:t=>{let s=e.fallbackModels.filter(e=>e!==t);h(e.id,{primaryModel:t,fallbackModels:s})},showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:s.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-1 mb-2",children:(0,t.jsxs)("div",{className:"bg-amber-50 text-amber-600 px-3 py-0.5 rounded-full text-[10px] font-bold border border-amber-100 flex items-center gap-1",children:[(0,t.jsx)(r.ArrowDown,{className:"w-3 h-3"}),"IF BUDGET EXCEEDED, TRY"]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Fallback Models"}),(0,t.jsx)(l.Select,{mode:"multiple",className:"w-full",placeholder:e.primaryModel?"Select fallback models":"Select a primary model first",value:e.fallbackModels,onChange:t=>h(e.id,{fallbackModels:t}),disabled:!e.primaryModel,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:i.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(a.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})}),e.fallbackModels.length>1&&(0,t.jsx)("div",{className:"text-[10px] text-gray-400 mt-1 ml-1",children:"Tried in order; first model still within its own budget is used"})]})]},e.id)}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]})}],128233);var d=e.i(28651);let c=[{value:"1h",label:"Hourly",resetHint:"Resets every hour"},{value:"24h",label:"Daily",resetHint:"Resets daily at midnight UTC"},{value:"7d",label:"Weekly",resetHint:"Resets every Sunday at midnight UTC"},{value:"30d",label:"Monthly",resetHint:"Resets on the 1st of every month at midnight UTC"}];e.s(["BudgetWindowsEditor",0,function({value:e,onChange:a}){let r=(t,s,l)=>{a(e.map((e,a)=>a===t?{...e,[s]:l}:e))};return(0,t.jsxs)("div",{children:[e.map((i,n)=>{let o=c.find(e=>e.value===i.budget_duration)?.resetHint;return(0,t.jsxs)("div",{style:{marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsx)(l.Select,{value:i.budget_duration,onChange:e=>r(n,"budget_duration",e),style:{width:130},options:c.map(e=>({value:e.value,label:e.label}))}),(0,t.jsx)(d.InputNumber,{step:.01,min:0,precision:2,value:i.max_budget??void 0,onChange:e=>r(n,"max_budget",e??null),placeholder:"Max spend ($)",style:{width:160},prefix:"$"}),(0,t.jsx)(s.Button,{type:"text",danger:!0,size:"small",onClick:()=>{a(e.filter((e,t)=>t!==n))},style:{padding:"0 4px"},children:"✕"})]}),o&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#888",marginTop:3,marginLeft:2},children:["↻ ",o]})]},n)}),(0,t.jsx)(s.Button,{size:"small",onClick:t=>{t.preventDefault(),a([...e,{budget_duration:"24h",max_budget:null}])},children:"+ Add Budget Window"})]})}],319312)},390605,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(602869),a=e.i(599724),r=e.i(482725),i=e.i(91739),n=e.i(500727),o=e.i(531516),d=e.i(696609);e.s(["default",0,({accessToken:e,selectedServers:c,toolPermissions:u,onChange:m,disabled:p=!1})=>{let{data:g=[]}=(0,n.useMCPServers)(),[h,x]=(0,s.useState)({}),[y,f]=(0,s.useState)({}),[b,_]=(0,s.useState)({}),[j,v]=(0,s.useState)({}),w=(0,s.useRef)(u);(0,s.useEffect)(()=>{w.current=u},[u]);let N=(0,s.useMemo)(()=>0===c.length?[]:g.filter(e=>c.includes(e.server_id)),[g,c]),k=async(e,t)=>{f(t=>({...t,[e]:!0})),_(t=>({...t,[e]:""}));try{let s=await (0,l.listMCPTools)(t,e);if(s.error)_(t=>({...t,[e]:s.message||"Failed to fetch tools"})),x(t=>({...t,[e]:[]}));else{let t=s.tools||[];x(s=>({...s,[e]:t}));let l=w.current;if(!l[e]&&t.length>0){let s=t.filter(e=>"delete"!==(0,d.classifyToolOp)(e.name,e.description||"")).map(e=>e.name);m({...l,[e]:s})}}}catch(t){console.error(`Error fetching tools for server ${e}:`,t),_(t=>({...t,[e]:"Failed to fetch tools"})),x(t=>({...t,[e]:[]}))}finally{f(t=>({...t,[e]:!1}))}};(0,s.useEffect)(()=>{N.forEach(t=>{h[t.server_id]||y[t.server_id]||k(t.server_id,e)})},[N,e]);let S=(e,t)=>{m({...u,[e]:t})};return 0===c.length?null:(0,t.jsx)("div",{className:"space-y-4",children:N.map(e=>{let s=e.server_name||e.alias||e.server_id,l=h[e.server_id]||[],n=u[e.server_id]||[],d=y[e.server_id],c=b[e.server_id],g=j[e.server_id]??"crud";return(0,t.jsxs)("div",{className:"border rounded-lg bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-4 border-b bg-white rounded-t-lg",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-semibold text-gray-900",children:s}),e.description&&(0,t.jsx)(a.Text,{className:"text-sm text-gray-500",children:e.description})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[!p&&l.length>0&&(0,t.jsx)(i.Radio.Group,{value:g,onChange:t=>v(s=>({...s,[e.server_id]:t.target.value})),size:"small",optionType:"button",buttonStyle:"solid",options:[{label:"Risk Groups",value:"crud"},{label:"Flat List",value:"flat"}]}),!p&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;let s;return s=h[t=e.server_id]||[],void m({...u,[t]:s.map(e=>e.name)})},disabled:d,children:"Select All"}),(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;return t=e.server_id,void m({...u,[t]:[]})},disabled:d,children:"Deselect All"})]})]})]}),(0,t.jsxs)("div",{className:"p-4",children:[d&&(0,t.jsxs)("div",{className:"flex items-center justify-center py-8",children:[(0,t.jsx)(r.Spin,{size:"large"}),(0,t.jsx)(a.Text,{className:"ml-3 text-gray-500",children:"Loading tools..."})]}),c&&!d&&(0,t.jsxs)("div",{className:"p-4 bg-red-50 border border-red-200 rounded-lg text-center",children:[(0,t.jsx)(a.Text,{className:"text-red-600 font-medium",children:"Unable to load tools"}),(0,t.jsx)(a.Text,{className:"text-sm text-red-500 mt-1",children:c})]}),!d&&!c&&l.length>0&&"crud"===g&&(0,t.jsx)(o.default,{tools:l,value:u[e.server_id]?n:void 0,onChange:t=>S(e.server_id,t),readOnly:p}),!d&&!c&&l.length>0&&"flat"===g&&(0,t.jsx)("div",{className:"space-y-2",children:l.map(s=>{let l=n.includes(s.name);return(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("input",{type:"checkbox",checked:l,onChange:()=>{if(p)return;let t=l?n.filter(e=>e!==s.name):[...n,s.name];S(e.server_id,t)},disabled:p,className:"mt-0.5"}),(0,t.jsx)("div",{className:"flex-1 min-w-0",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900",children:s.name}),(0,t.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["- ",s.description||"No description"]})]})})]},s.name)})}),!d&&!c&&0===l.length&&(0,t.jsx)("div",{className:"text-center py-6",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"No tools available"})})]})]},e.server_id)})})}])},109034,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(135214);let r=(0,s.createQueryKeys)("tags");e.s(["useTags",0,()=>{let{accessToken:e,userId:s,userRole:i}=(0,a.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,l.tagListCall)(e),enabled:!!(e&&s&&i)})}])},533882,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(250980),a=e.i(797672),r=e.i(68155),i=e.i(304967),n=e.i(629569),o=e.i(599724),d=e.i(269200),c=e.i(427612),u=e.i(64848),m=e.i(942232),p=e.i(496020),g=e.i(977572),h=e.i(992619),x=e.i(727749);e.s(["default",0,({accessToken:e,initialModelAliases:y={},onAliasUpdate:f,showExampleConfig:b=!0})=>{let[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)({aliasName:"",targetModel:""}),[N,k]=(0,s.useState)(null);(0,s.useEffect)(()=>{j(Object.entries(y).map(([e,t],s)=>({id:`${s}-${e}`,aliasName:e,targetModel:t})))},[y]);let S=()=>{if(!N)return;if(!N.aliasName||!N.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.id!==N.id&&e.aliasName===N.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=_.map(e=>e.id===N.id?N:e);j(e),k(null);let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias updated successfully")},C=()=>{k(null)},T=_.reduce((e,t)=>(e[t.aliasName]=t.targetModel,e),{});return(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:v.aliasName,onChange:e=>w({...v,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model"}),(0,t.jsx)(h.default,{accessToken:e,value:v.targetModel,placeholder:"Select target model",onChange:e=>w({...v,targetModel:e}),showLabel:!1})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:()=>{if(!v.aliasName||!v.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.aliasName===v.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=[..._,{id:`${Date.now()}-${v.aliasName}`,aliasName:v.aliasName,targetModel:v.targetModel}];j(e),w({aliasName:"",targetModel:""});let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias added successfully")},disabled:!v.aliasName||!v.targetModel,className:`flex items-center px-4 py-2 rounded-md text-sm ${!v.aliasName||!v.targetModel?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(l.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(d.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(c.TableHead,{children:(0,t.jsxs)(p.TableRow,{children:[(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Target Model"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(m.TableBody,{children:[_.map(s=>(0,t.jsx)(p.TableRow,{className:"h-8",children:N&&N.id===s.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:N.aliasName,onChange:e=>k({...N,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)(h.default,{accessToken:e,value:N.targetModel,onChange:e=>k({...N,targetModel:e}),showLabel:!1,style:{height:"32px"}})}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:S,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:C,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-900",children:s.aliasName}),(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-500",children:s.targetModel}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{k({...s})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(a.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>{var e;let t,l;return e=s.id,j(t=_.filter(t=>t.id!==e)),l={},void(t.forEach(e=>{l[e.aliasName]=e.targetModel}),f&&f(l),x.default.success("Alias deleted successfully"))},className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(r.TrashIcon,{className:"w-3 h-3"})})]})})]})},s.id)),0===_.length&&(0,t.jsx)(p.TableRow,{children:(0,t.jsx)(g.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),b&&(0,t.jsxs)(i.Card,{children:[(0,t.jsx)(n.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(o.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["model_aliases:",0===Object.keys(T).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"  # No aliases configured yet"]}):Object.entries(T).map(([e,s])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'  "',e,'": "',s,'"']},e))]})})]})]})}])},266484,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(592968),a=e.i(312361),r=e.i(827252),i=e.i(994388),n=e.i(304967),o=e.i(779241),d=e.i(988297),c=e.i(68155),u=e.i(810757),m=e.i(477386),p=e.i(557662),g=e.i(555987),h=e.i(435451);let{Option:x}=s.Select;e.s(["default",0,({value:e=[],onChange:y,disabledCallbacks:f=[],onDisabledCallbacksChange:b})=>{let _=Object.entries(p.callbackInfo).filter(([e,t])=>t.supports_key_team_logging).map(([e,t])=>e),j=Object.keys(p.callbackInfo),v=e=>{y?.(e)},w=(t,s,l)=>{let a=[...e];if("callback_name"===s){let e=p.callback_map[l]||l;a[t]={...a[t],[s]:e,callback_vars:{}}}else a[t]={...a[t],[s]:l};v(a)},N=(t,s,l)=>{let a=[...e];a[t]={...a[t],callback_vars:{...a[t].callback_vars,[s]:l}},v(a)};return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(m.BanIcon,{className:"w-5 h-5 text-red-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Disabled Callbacks"}),(0,t.jsx)(l.Tooltip,{title:"Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Disabled Callbacks"}),(0,t.jsx)(s.Select,{mode:"multiple",placeholder:"Select callbacks to disable",value:f,onChange:e=>{let t=(0,p.mapDisplayToInternalNames)(e);b?.(t)},style:{width:"100%"},optionLabelProp:"label",children:j.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Select callbacks that should be disabled for this key. These callbacks will not receive any logging data."})]})]}),(0,t.jsx)(a.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(u.CogIcon,{className:"w-5 h-5 text-blue-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Logging Integrations"}),(0,t.jsx)(l.Tooltip,{title:"Configure callback logging integrations for this team.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsx)(i.Button,{variant:"secondary",onClick:()=>{v([...e,{callback_name:"",callback_type:"success",callback_vars:{}}])},icon:d.PlusIcon,size:"sm",className:"hover:border-blue-400 hover:text-blue-500",type:"button",children:"Add Integration"})]}),(0,t.jsx)("div",{className:"space-y-4",children:e.map((a,d)=>{let u=a.callback_name?Object.entries(p.callback_map).find(([e,t])=>t===a.callback_name)?.[0]:void 0,m=u?(0,g.resolveLogoSrc)(p.callbackInfo[u]?.logo):null;return(0,t.jsxs)(n.Card,{className:"border border-gray-200 shadow-xs hover:shadow-md transition-shadow duration-200",decoration:"top",decorationColor:"blue",children:[(0,t.jsxs)("div",{className:"flex justify-between items-start mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[m&&(0,t.jsx)("img",{src:m,alt:u,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("span",{className:"text-sm font-medium",children:[u||"New Integration"," Configuration"]})]}),(0,t.jsx)(i.Button,{variant:"light",onClick:()=>{v(e.filter((e,t)=>t!==d))},icon:c.TrashIcon,size:"xs",color:"red",className:"hover:bg-red-50",type:"button",children:"Remove"})]}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Integration Type"}),(0,t.jsx)(s.Select,{value:u,placeholder:"Select integration",onChange:e=>w(d,"callback_name",e),className:"w-full",optionLabelProp:"label",children:_.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Event Type"}),(0,t.jsxs)(s.Select,{value:a.callback_type,onChange:e=>w(d,"callback_type",e),className:"w-full",children:[(0,t.jsx)(x,{value:"success",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),(0,t.jsx)("span",{children:"Success Only"})]})}),(0,t.jsx)(x,{value:"failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-red-500 rounded-full"}),(0,t.jsx)("span",{children:"Failure Only"})]})}),(0,t.jsx)(x,{value:"success_and_failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),(0,t.jsx)("span",{children:"Success & Failure"})]})})]})]})]}),((e,s)=>{if(!e.callback_name)return null;let a=Object.entries(p.callback_map).find(([t,s])=>s===e.callback_name)?.[0];if(!a)return null;let i=p.callbackInfo[a]?.dynamic_params||{};return 0===Object.keys(i).length?null:(0,t.jsxs)("div",{className:"mt-6 pt-4 border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2 mb-4",children:[(0,t.jsx)("div",{className:"w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center",children:(0,t.jsx)("div",{className:"w-1.5 h-1.5 bg-blue-500 rounded-full"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Integration Parameters"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-4",children:Object.entries(i).map(([a,i])=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 capitalize flex items-center space-x-1",children:[(0,t.jsx)("span",{children:a.replace(/_/g," ")}),(0,t.jsx)(l.Tooltip,{title:`Environment variable reference recommended: os.environ/${a.toUpperCase()}`,children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),"password"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Sensitive"}),"number"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Number"})]}),"number"===i&&(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Value must be between 0 and 1"}),"number"===i?(0,t.jsx)(h.default,{step:.01,width:400,placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)}):(0,t.jsx)(o.TextInput,{type:"password"===i?"password":"text",placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)})]},a))})]})})(a,d)]})]},d)})}),0===e.length&&(0,t.jsxs)("div",{className:"text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50",children:[(0,t.jsx)(u.CogIcon,{className:"w-12 h-12 text-gray-300 mb-3 mx-auto"}),(0,t.jsx)("div",{className:"text-base font-medium mb-1",children:"No logging integrations configured"}),(0,t.jsx)("div",{className:"text-sm text-gray-400",children:'Click "Add Integration" to configure logging for this team'})]})]})}])},651904,e=>{"use strict";var t=e.i(843476),s=e.i(599724),l=e.i(266484);e.s(["default",0,function({value:e,onChange:a,premiumUser:r=!1,disabledCallbacks:i=[],onDisabledCallbacksChange:n}){return r?(0,t.jsx)(l.default,{value:e,onChange:a,disabledCallbacks:i,onDisabledCallbacksChange:n}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex flex-wrap gap-2 mb-3",children:[(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ langfuse-logging"}),(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ datadog-logging"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(s.Text,{className:"text-sm text-yellow-800",children:["Setting Key/Team logging settings is a LiteLLM Enterprise feature. Global Logging Settings are available for all free users. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})}])},460285,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(404206),a=e.i(723731),r=e.i(653824),i=e.i(881073),n=e.i(197647),o=e.i(602869),d=e.i(158392),c=e.i(419470),u=e.i(695411);let m=(0,s.forwardRef)(({accessToken:e,value:m,onChange:p,modelData:g},h)=>{let[x,y]=(0,s.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[f,b]=(0,s.useState)([]),[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)([]),[N,k]=(0,s.useState)([]),[S,C]=(0,s.useState)({}),[T,I]=(0,s.useState)({}),A=(0,s.useRef)(!1),L=(0,s.useRef)(null);(0,s.useEffect)(()=>{let e=m?.router_settings?JSON.stringify({routing_strategy:m.router_settings.routing_strategy,fallbacks:m.router_settings.fallbacks,enable_tag_filtering:m.router_settings.enable_tag_filtering}):null;if(A.current&&e===L.current){A.current=!1;return}if(A.current&&e!==L.current&&(A.current=!1),e!==L.current)if(L.current=e,m?.router_settings){let e=m.router_settings,{fallbacks:t,...s}=e;y({routerSettings:s,selectedStrategy:e.routing_strategy||null,enableTagFiltering:e.enable_tag_filtering??!1});let l=e.fallbacks||[];b(l),j(l&&0!==l.length?l.map((e,t)=>{let[s,l]=Object.entries(e)[0];return{id:(t+1).toString(),primaryModel:s||null,fallbackModels:l||[]}}):[{id:"1",primaryModel:null,fallbackModels:[]}])}else y({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),b([]),j([{id:"1",primaryModel:null,fallbackModels:[]}])},[m]),(0,s.useEffect)(()=>{e&&(0,o.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),C(t);let s=e.fields.find(e=>"routing_strategy"===e.field_name);s?.options&&k(s.options),e.routing_strategy_descriptions&&I(e.routing_strategy_descriptions)}})},[e]),(0,s.useEffect)(()=>{e&&(async()=>{try{let t=await (0,u.fetchAvailableModels)(e);w(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}})()},[e]);let F=()=>{let e=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),t=new Set(["model_group_alias","retry_policy"]),s=Object.fromEntries(Object.entries({...x.routerSettings,enable_tag_filtering:x.enableTagFiltering,routing_strategy:x.selectedStrategy,fallbacks:f.length>0?f:null}).map(([s,l])=>{if("routing_strategy_args"!==s&&"routing_strategy"!==s&&"enable_tag_filtering"!==s&&"fallbacks"!==s){let a=document.querySelector(`input[name="${s}"]`);if(a){if(void 0!==a.value&&""!==a.value){let r=((s,l,a)=>{if(null==l)return a;let r=String(l).trim();if(""===r||"null"===r.toLowerCase())return null;if(e.has(s)){let e=Number(r);return Number.isNaN(e)?a:e}if(t.has(s)){if(""===r)return null;try{return JSON.parse(r)}catch{return a}}return"true"===r.toLowerCase()||"false"!==r.toLowerCase()&&r})(s,a.value,l);return[s,r]}return[s,null]}}else if("routing_strategy"===s)return[s,x.selectedStrategy];else if("enable_tag_filtering"===s)return[s,x.enableTagFiltering];else if("fallbacks"===s)return[s,f.length>0?f:null];else if("routing_strategy_args"===s&&"latency-based-routing"===x.selectedStrategy){let e=document.querySelector('input[name="lowest_latency_buffer"]'),t=document.querySelector('input[name="ttl"]'),s={};return e?.value&&(s.lowest_latency_buffer=Number(e.value)),t?.value&&(s.ttl=Number(t.value)),["routing_strategy_args",Object.keys(s).length>0?s:null]}return[s,l]}).filter(e=>null!=e)),l=(e,t=!1)=>null==e||"object"==typeof e&&!Array.isArray(e)&&0===Object.keys(e).length||t&&("number"!=typeof e||Number.isNaN(e))?null:e;return{routing_strategy:l(s.routing_strategy),allowed_fails:l(s.allowed_fails,!0),cooldown_time:l(s.cooldown_time,!0),num_retries:l(s.num_retries,!0),timeout:l(s.timeout,!0),retry_after:l(s.retry_after,!0),fallbacks:f.length>0?f:null,context_window_fallbacks:l(s.context_window_fallbacks),retry_policy:l(s.retry_policy),model_group_alias:l(s.model_group_alias),enable_tag_filtering:x.enableTagFiltering,routing_strategy_args:l(s.routing_strategy_args)}};(0,s.useEffect)(()=>{if(!p)return;let e=setTimeout(()=>{A.current=!0,p({router_settings:F()})},100);return()=>clearTimeout(e)},[x,f]);let M=Array.from(new Set(v.map(e=>e.model_group))).sort();return((0,s.useImperativeHandle)(h,()=>({getValue:()=>({router_settings:F()})})),e)?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(r.TabGroup,{className:"w-full",children:[(0,t.jsxs)(i.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(n.Tab,{value:"2",children:"Fallbacks"})]}),(0,t.jsxs)(a.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(d.default,{value:x,onChange:y,routerFieldsMetadata:S,availableRoutingStrategies:N,routingStrategyDescriptions:T})}),(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(c.FallbackSelectionForm,{groups:_,onGroupsChange:e=>{j(e),b(e.filter(e=>e.primaryModel&&e.fallbackModels.length>0).map(e=>({[e.primaryModel]:e.fallbackModels})))},availableModels:M,maxGroups:5})})]})]})}):null});m.displayName="RouterSettingsAccordion",e.s(["default",0,m])},575260,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(482725),a=e.i(56456);e.s(["default",0,({projects:e,value:r,onChange:i,disabled:n,loading:o,teamId:d})=>{let c=d?e?.filter(e=>e.team_id===d):e;return(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"Search or select a project",value:r,onChange:i,disabled:n,loading:o,allowClear:!0,notFoundContent:o?(0,t.jsx)(l.Spin,{indicator:(0,t.jsx)(a.LoadingOutlined,{spin:!0}),size:"small"}):void 0,filterOption:(e,t)=>{if(!t)return!1;let s=c?.find(e=>e.project_id===t.key);if(!s)return!1;let l=e.toLowerCase().trim(),a=(s.project_alias||"").toLowerCase(),r=(s.project_id||"").toLowerCase();return a.includes(l)||r.includes(l)},optionFilterProp:"children",children:!o&&c?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.project_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.project_alias||e.project_id})," ",(0,t.jsxs)("span",{className:"text-gray-500",children:["(",e.project_id,")"]})]},e.project_id))})}])},364769,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(237016),a=e.i(464571),r=e.i(888259);e.s(["default",0,({apiKey:e})=>{let[i,n]=(0,s.useState)(!1);return(0,t.jsxs)("div",{children:[(0,t.jsxs)("p",{className:"mb-2",children:["Please save this secret key somewhere safe and accessible. For security reasons,"," ",(0,t.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]}),(0,t.jsx)("p",{className:"text-sm text-gray-600 mt-3 mb-1",children:"Virtual Key:"}),(0,t.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,t.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal",margin:0},children:e})}),(0,t.jsx)(l.CopyToClipboard,{text:e,onCopy:()=>{n(!0),r.default.success("Key copied to clipboard"),setTimeout(()=>n(!1),2e3)},children:(0,t.jsx)(a.Button,{type:"primary",style:{marginTop:12},children:i?"Copied!":"Copy Virtual Key"})})]})}])},702597,e=>{"use strict";var t=e.i(843476),s=e.i(207082),l=e.i(109799),a=e.i(510674),r=e.i(109034),i=e.i(292639),n=e.i(135214),o=e.i(500330),d=e.i(827252),c=e.i(912598),u=e.i(677667),m=e.i(130643),p=e.i(898667),g=e.i(994388),h=e.i(309426),x=e.i(350967),y=e.i(599724),f=e.i(779241),b=e.i(629569),_=e.i(464571),j=e.i(808613),v=e.i(311451),w=e.i(212931),N=e.i(91739),k=e.i(199133),S=e.i(790848),C=e.i(262218),T=e.i(592968),I=e.i(898586),A=e.i(374009),L=e.i(271645),F=e.i(708347),M=e.i(552130),O=e.i(557662),E=e.i(9314),P=e.i(860585),B=e.i(82946),$=e.i(392110),R=e.i(533882),D=e.i(844565),V=e.i(651904),z=e.i(939510),U=e.i(460285),G=e.i(663435),K=e.i(363256),q=e.i(575260),W=e.i(371455),H=e.i(128233),Q=e.i(319312),J=e.i(355619),Y=e.i(75921),X=e.i(234713),Z=e.i(390605),ee=e.i(727749),et=e.i(602869),es=e.i(364769),el=e.i(435451),ea=e.i(916940);let{Option:er}=k.Select,ei=async(e,t,s,l)=>{try{if(null===e||null===t)return[];if(null!==s)return(await (0,et.modelAvailableCall)(s,e,t,!0,l,!0)).data.map(e=>e.id);return[]}catch(e){return console.error("Error fetching user models:",e),[]}},en=async(e,t,s,l)=>{try{if(null===e||null===t)return;if(null!==s){let a=(await (0,et.modelAvailableCall)(s,e,t)).data.map(e=>e.id);l(a)}}catch(e){console.error("Error fetching user models:",e)}};e.s(["default",0,({team:e,teams:eo,data:ed,addKey:ec,autoOpenCreate:eu,prefillData:em})=>{let{accessToken:ep,userId:eg,userRole:eh,premiumUser:ex}=(0,n.default)(),ey=ex||null!=eh&&F.rolesWithWriteAccess.includes(eh),{data:ef,isLoading:eb}=(0,l.useOrganizations)(),{data:e_,isLoading:ej}=(0,a.useProjects)(),{data:ev}=(0,i.useUISettings)(),{data:ew}=(0,r.useTags)(),eN=!!ev?.values?.enable_projects_ui,ek=!!ev?.values?.disable_custom_api_keys,eS=ew?Object.values(ew).map(e=>({value:e.name,label:e.name})):[],eC=(0,c.useQueryClient)(),[eT]=j.Form.useForm(),[eI,eA]=(0,L.useState)(!1),[eL,eF]=(0,L.useState)(null),[eM,eO]=(0,L.useState)(null),[eE,eP]=(0,L.useState)([]),[eB,e$]=(0,L.useState)([]),[eR,eD]=(0,L.useState)("you"),[eV,ez]=(0,L.useState)(!1),[eU,eG]=(0,L.useState)(null),[eK,eq]=(0,L.useState)([]),[eW,eH]=(0,L.useState)([]),[eQ,eJ]=(0,L.useState)([]),[eY,eX]=(0,L.useState)([]),[eZ,e0]=(0,L.useState)(e),[e1,e2]=(0,L.useState)(null),[e4,e3]=(0,L.useState)(null),[e5,e6]=(0,L.useState)(!1),[e7,e9]=(0,L.useState)(null),[e8,te]=(0,L.useState)({}),[tt,ts]=(0,L.useState)([]),[tl,ta]=(0,L.useState)(!1),[tr,ti]=(0,L.useState)([]),[tn,to]=(0,L.useState)([]),[td,tc]=(0,L.useState)("llm_api"),[tu,tm]=(0,L.useState)({}),[tp,tg]=(0,L.useState)(!1),[th,tx]=(0,L.useState)("30d"),[ty,tf]=(0,L.useState)(null),[tb,t_]=(0,L.useState)([]),[tj,tv]=(0,L.useState)({}),[tw,tN]=(0,L.useState)(0),[tk,tS]=(0,L.useState)(0),[tC,tT]=(0,L.useState)([]),[tI,tA]=(0,L.useState)(null),tL=()=>{eA(!1),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)},tF=()=>{eA(!1),eF(null),e0(null),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)};(0,L.useEffect)(()=>{eg&&eh&&ep&&en(eg,eh,ep,eP)},[ep,eg,eh]),(0,L.useEffect)(()=>{ep&&(0,et.getAgentsList)(ep).then(e=>tT(e?.agents||[])).catch(()=>tT([]))},[ep]),(0,L.useEffect)(()=>{let e=async()=>{try{let e=(await (0,et.getPoliciesList)(ep)).policies.map(e=>e.policy_name);eH(e)}catch(e){console.error("Failed to fetch policies:",e)}},t=async()=>{try{let e=await (0,et.getPromptsList)(ep);eJ(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}};(async()=>{try{let e=(await (0,et.getGuardrailsList)(ep)).guardrails.map(e=>e.guardrail_name);eq(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})(),e(),t()},[ep]),(0,L.useEffect)(()=>{(async()=>{try{if(ep){let e=sessionStorage.getItem("possibleUserRoles");if(e)te(JSON.parse(e));else{let e=await (0,et.getPossibleUserRoles)(ep);sessionStorage.setItem("possibleUserRoles",JSON.stringify(e)),te(e)}}}catch(e){console.error("Error fetching possible user roles:",e)}})()},[ep]),(0,L.useEffect)(()=>{if(eu&&!eV&&eo&&eh&&F.rolesWithWriteAccess.includes(eh)&&(eA(!0),ez(!0),em)){if(em.owned_by&&("another_user"===em.owned_by&&"Admin"!==eh?eD("you"):eD(em.owned_by)),em.team_id){let e=eo?.find(e=>e.team_id===em.team_id)||null;e&&(e0(e),eT.setFieldsValue({team_id:em.team_id}))}em.key_alias&&eT.setFieldsValue({key_alias:em.key_alias}),em.models&&em.models.length>0&&eG(em.models),em.key_type&&(tc(em.key_type),eT.setFieldsValue({key_type:em.key_type}))}},[eu,em,eo,eV,eT,eh]);let tM=eB.includes("no-default-models")&&!eZ,tO=async e=>{try{let t,l=e?.key_alias??"",a=e?.team_id??null;if((ed?.filter(e=>e.team_id===a).map(e=>e.key_alias)??[]).includes(l))throw Error(`Key alias ${l} already exists for team with ID ${a}, please provide another key alias`);if(ee.default.info("Making API Call"),eA(!0),"you"===eR)e.user_id=eg;else if("agent"===eR){if(!tI)return void ee.default.fromBackend("Please select an agent");e.agent_id=tI}let r={};try{r=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}if("service_account"===eR&&(r.service_account_id=e.key_alias),eY.length>0&&(r={...r,logging:eY.filter(e=>e.callback_name)}),tn.length>0){let e=(0,O.mapDisplayToInternalNames)(tn);r={...r,litellm_disabled_callbacks:e}}if(tp&&(e.auto_rotate=!0,e.rotation_interval=th),e.duration&&""!==e.duration.trim()||(e.duration=null),e.metadata=JSON.stringify(r),e.disable_global_guardrails||delete e.disable_global_guardrails,e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission={vector_stores:e.allowed_vector_store_ids},delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{servers:t,accessGroups:s}=e.allowed_mcp_servers_and_groups;t&&t.length>0&&(e.object_permission.mcp_servers=t),s&&s.length>0&&(e.object_permission.mcp_access_groups=s),delete e.allowed_mcp_servers_and_groups}let i=e.mcp_tool_permissions||{};if(Object.keys(i).length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_tool_permissions=i),delete e.mcp_tool_permissions,e.allowed_mcp_access_groups&&e.allowed_mcp_access_groups.length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_access_groups=e.allowed_mcp_access_groups,delete e.allowed_mcp_access_groups),e.allowed_agents_and_groups&&(e.allowed_agents_and_groups.agents?.length>0||e.allowed_agents_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{agents:t,accessGroups:s}=e.allowed_agents_and_groups;t&&t.length>0&&(e.object_permission.agents=t),s&&s.length>0&&(e.object_permission.agent_access_groups=s),delete e.allowed_agents_and_groups}Object.keys(tu).length>0&&(e.aliases=JSON.stringify(tu)),ty?.router_settings&&Object.values(ty.router_settings).some(e=>null!=e&&""!==e)&&(e.router_settings=ty.router_settings);let n=tb.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);n.length>0&&(e.budget_limits=n),Object.keys(tj).length>0&&(e.budget_fallbacks=tj),t="service_account"===eR?await (0,et.keyCreateServiceAccountCall)(ep,e):await (0,et.keyCreateCall)(ep,eg,e),ec(t),eC.invalidateQueries({queryKey:s.keyKeys.lists()}),eF(t.key),eO(t.soft_budget),ee.default.success("Virtual Key Created"),eT.resetFields(),t_([]),tv({}),tN(e=>e+1),localStorage.removeItem("userData"+eg)}catch(t){let e=(e=>{let t;if(!(t=!e||"object"!=typeof e||e instanceof Error?String(e):JSON.stringify(e)).includes("/key/generate")&&!t.includes("KeyManagementRoutes.KEY_GENERATE"))return`Error creating the key: ${e}`;let s=t;try{if(!e||"object"!=typeof e||e instanceof Error){let e=t.match(/\{[\s\S]*\}/);if(e){let t=JSON.parse(e[0]),l=t?.error||t;l?.message&&(s=l.message)}}else{let t=e?.error||e;t?.message&&(s=t.message)}}catch(e){}return t.includes("team_member_permission_error")||s.includes("Team member does not have permissions")?"Team member does not have permission to generate key for this team. Ask your proxy admin to configure the team member permission settings.":`Error creating the key: ${e}`})(t);ee.default.fromBackend(e)}};(0,L.useEffect)(()=>{if(e4){let e=e_?.find(e=>e.project_id===e4);e$(e?.models??[]),eT.setFieldValue("models",[]);return}eg&&eh&&ep&&ei(eg,eh,ep,eZ?.team_id??null).then(e=>{e$(Array.from(new Set([...eZ?.models??[],...e])))}),eU||eT.setFieldValue("models",[]),eT.setFieldValue("allowed_mcp_servers_and_groups",{servers:[],accessGroups:[]})},[eZ,e4,ep,eg,eh,eT]),(0,L.useEffect)(()=>{if(!eU||0===eU.length||!eB||0===eB.length)return;let e=eU.filter(e=>eB.includes(e));e.length>0&&eT.setFieldsValue({models:e}),eG(null)},[eU,eB,eT]),(0,L.useEffect)(()=>{if(!e4||!eo)return;let e=e_?.find(e=>e.project_id===e4);if(!e?.team_id||eZ?.team_id===e.team_id)return;let t=eo.find(t=>t.team_id===e.team_id)||null;t&&(e0(t),eT.setFieldValue("team_id",t.team_id))},[eo,e4,e_]);let tE=async e=>{if(!e)return void ts([]);ta(!0);try{let t=new URLSearchParams;if(t.append("user_email",e),null==ep)return;let s=(await (0,et.userFilterUICall)(ep,t)).map(e=>({label:`${e.user_email} (${e.user_id})`,value:e.user_id,user:e}));ts(s)}catch(e){console.error("Error fetching users:",e),ee.default.fromBackend("Failed to search for users")}finally{ta(!1)}},tP=(0,L.useCallback)((0,A.default)(e=>tE(e),300),[ep]);return(0,t.jsxs)("div",{children:[eh&&F.rolesWithWriteAccess.includes(eh)&&(0,t.jsx)(g.Button,{className:"mx-auto",onClick:()=>eA(!0),"data-testid":"create-key-button",children:"+ Create New Key"}),(0,t.jsx)(w.Modal,{open:eI,width:1e3,footer:null,onOk:tL,onCancel:tF,children:(0,t.jsxs)(j.Form,{form:eT,onFinish:tO,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Ownership"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Owned By"," ",(0,t.jsx)(T.Tooltip,{title:"Select who will own this Virtual Key",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),className:"mb-4",children:(0,t.jsxs)(N.Radio.Group,{onChange:e=>eD(e.target.value),value:eR,children:[(0,t.jsx)(N.Radio,{value:"you",children:"You"}),(0,t.jsx)(N.Radio,{value:"service_account",children:"Service Account"}),"Admin"===eh&&(0,t.jsx)(N.Radio,{value:"another_user",children:"Another User"}),(0,t.jsxs)(N.Radio,{value:"agent",children:["Agent ",(0,t.jsx)(C.Tag,{color:"purple",children:"New"})]})]})}),"another_user"===eR&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["User ID"," ",(0,t.jsx)(T.Tooltip,{title:"The user who will own this key and be responsible for its usage",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"user_id",className:"mt-4",rules:[{required:"another_user"===eR,message:"Please input the user ID of the user you are assigning the key to"}],children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",marginBottom:"8px"},children:[(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Type email to search for users",filterOption:!1,onSearch:e=>{tP(e)},onSelect:(e,t)=>{let s;return s=t.user,void eT.setFieldsValue({user_id:s.user_id})},options:tt,loading:tl,allowClear:!0,style:{width:"100%"},notFoundContent:tl?"Searching...":"No users found"}),(0,t.jsx)(_.Button,{onClick:()=>e6(!0),style:{marginLeft:"8px"},children:"Create User"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Search by email to find users"})]})}),"agent"===eR&&(0,t.jsxs)("div",{className:"mt-4 p-4 bg-purple-50 border border-purple-200 rounded-md",children:[(0,t.jsx)("div",{className:"mb-3",children:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:["Select Agent ",(0,t.jsx)("span",{className:"text-red-500",children:"*"})]})}),(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Select an agent",style:{width:"100%"},value:tI,onChange:e=>tA(e),filterOption:(e,t)=>t?.label?.toLowerCase().includes(e.toLowerCase()),options:tC.map(e=>({label:e.agent_name||e.agent_id,value:e.agent_id}))}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"This key will be used by the selected agent to make requests to LiteLLM"})]}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(T.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",className:"mt-4",children:(0,t.jsx)(K.default,{organizations:ef,loading:eb,disabled:"Admin"!==eh,onChange:e=>{e2(e||null),e0(null),e3(null),eT.setFieldValue("team_id",void 0),eT.setFieldValue("project_id",void 0)}})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Team"," ",(0,t.jsx)(T.Tooltip,{title:"The team this key belongs to, which determines available models and budget limits",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"team_id",initialValue:e?e.team_id:null,className:"mt-4",rules:[{required:"service_account"===eR,message:"Please select a team for the service account"}],help:"service_account"===eR?"required":"",children:(0,t.jsx)(G.default,{disabled:null!==e4,organizationId:e1,onTeamSelect:e=>{e0(e),e3(null),eT.setFieldValue("project_id",void 0),e?.organization_id?(e2(e.organization_id),eT.setFieldValue("organization_id",e.organization_id)):e||(e2(null),eT.setFieldValue("organization_id",void 0))}})}),eN&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Project"," ",(0,t.jsx)(T.Tooltip,{title:"Assign this key to a project. Selecting a project will lock the team to the project's team.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"project_id",className:"mt-4",children:(0,t.jsx)(q.default,{projects:e_,teamId:eZ?.team_id,loading:ej||!eo,onChange:e=>{if(!e){e3(null),e0(null),eT.setFieldValue("team_id",void 0);return}e3(e)}})})]}),tM&&(0,t.jsx)("div",{className:"mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md",children:(0,t.jsx)(y.Text,{className:"text-blue-800 text-sm",children:"Please select a team to continue configuring your Virtual Key. If you do not see any teams, please contact your Proxy Admin to either provide you with access to models or to add you to a team."})}),!tM&&(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Details"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["you"===eR||"another_user"===eR?"Key Name":"Service Account ID"," ",(0,t.jsx)(T.Tooltip,{title:"you"===eR||"another_user"===eR?"A descriptive name to identify this key":"Unique identifier for this service account",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_alias",rules:[{required:!0,message:`Please input a ${"you"===eR?"key name":"service account ID"}`}],help:"required",children:(0,t.jsx)(f.TextInput,{placeholder:""})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Models"," ",(0,t.jsx)(T.Tooltip,{title:"Select which models this key can access. Choose 'All Team Models' to grant access to all models available to the team. Leave empty to allow access to all models.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",rules:[],help:"management"===td||"read_only"===td?"Models field is disabled for this key type":"optional - leave empty to allow access to all models",className:"mt-4",children:(0,t.jsxs)(k.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:"management"===td||"read_only"===td,onChange:e=>{e.includes("all-team-models")&&eT.setFieldsValue({models:["all-team-models"]})},children:[!e4&&(0,t.jsx)(er,{value:"all-team-models",children:"All Team Models"},"all-team-models"),eB.map(e=>(0,t.jsx)(er,{value:e,children:(0,J.getModelDisplayName)(e)},e))]})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Key Type"," ",(0,t.jsx)(T.Tooltip,{title:"Select the type of key to determine what routes and operations this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_type",initialValue:"llm_api",className:"mt-4",children:(0,t.jsxs)(k.Select,{defaultValue:"llm_api",placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",onChange:e=>{tc(e),("management"===e||"read_only"===e)&&eT.setFieldsValue({models:[]})},children:[(0,t.jsx)(er,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"AI APIs"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(er,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Management"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only management routes (user/team/key management)"})]})}),(0,t.jsx)(er,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Full Access"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})})]})})]}),!tM&&(0,t.jsx)("div",{className:"mb-8",children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)(b.Title,{className:"m-0",children:"Optional Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum amount in USD this key can spend. When reached, the key will be blocked from making further requests",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",help:`Budget cannot exceed team max budget: $${e?.max_budget!==null&&e?.max_budget!==void 0?e?.max_budget:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.max_budget&&s>e.max_budget)throw Error(`Budget cannot exceed team max budget: $${(0,o.formatNumberWithCommas)(e.max_budget,4)}`)}}],children:(0,t.jsx)(el.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(T.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",help:`Team Reset Budget: ${e?.budget_duration!==null&&e?.budget_duration!==void 0?e?.budget_duration:"None"}`,children:(0,t.jsx)(P.default,{onChange:e=>eT.setFieldValue("budget_duration",e)})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(T.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(Q.BudgetWindowsEditor,{value:tb,onChange:t_})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(T.Tooltip,{title:"When a model exceeds its per-model budget (model_max_budget), requests automatically reroute to fallback models instead of failing. Configure per-model budgets in Advanced Settings.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(H.BudgetFallbacksEditor,{value:tj,onChange:tv,availableModels:eB},tw)}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Tokens per minute Limit (TPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of tokens this key can process per minute. Helps control usage and costs",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tpm_limit",help:`TPM cannot exceed team TPM limit: ${e?.tpm_limit!==null&&e?.tpm_limit!==void 0?e?.tpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.tpm_limit&&s>e.tpm_limit)throw Error(`TPM limit cannot exceed team TPM limit: ${e.tpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"tpm",name:"tpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Requests per minute Limit (RPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of API requests this key can make per minute. Helps prevent abuse and manage load",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"rpm_limit",help:`RPM cannot exceed team RPM limit: ${e?.rpm_limit!==null&&e?.rpm_limit!==void 0?e?.rpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.rpm_limit&&s>e.rpm_limit)throw Error(`RPM limit cannot exceed team RPM limit: ${e.rpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"rpm",name:"rpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:ey?"Select existing guardrails or enter new ones":"Premium feature - Upgrade to set guardrails by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter guardrails":"Premium feature - Upgrade to set guardrails by key",options:eK.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"disable_global_guardrails",className:"mt-4",valuePropName:"checked",help:ey?"Bypass global guardrails for this key":"Premium feature - Upgrade to disable global guardrails by key",children:(0,t.jsx)(S.Switch,{disabled:!ey,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(T.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",className:"mt-4",help:ex?"Select existing policies or enter new ones":"Premium feature - Upgrade to set policies by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter policies":"Premium feature - Upgrade to set policies by key",options:eW.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Prompts"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific prompt templates",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/prompt_management",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"prompts",className:"mt-4",help:ex?"Select existing prompts or enter new ones":"Premium feature - Upgrade to set prompts by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:eQ.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(T.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",className:"mt-4",help:"Select access groups to assign to this key",children:(0,t.jsx)(E.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Pass Through Routes"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific pass through routes",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"allowed_passthrough_routes",className:"mt-4",help:ex?"Select existing pass through routes or enter new ones":"Premium feature - Upgrade to set pass through routes by key",children:(0,t.jsx)(D.default,{onChange:e=>eT.setFieldValue("allowed_passthrough_routes",e),value:eT.getFieldValue("allowed_passthrough_routes"),accessToken:ep,placeholder:ex?"Select or enter pass through routes":"Premium feature - Upgrade to set pass through routes by key",disabled:!ex,teamId:eZ?eZ.team_id:null})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,t.jsx)(T.Tooltip,{title:"Select which vector stores this key can access. If none selected, the key will have access to all available vector stores",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this key can access. Leave empty for access to all vector stores",children:(0,t.jsx)(ea.default,{onChange:e=>eT.setFieldValue("allowed_vector_store_ids",e),value:eT.getFieldValue("allowed_vector_store_ids"),accessToken:ep,placeholder:"Select vector stores (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Metadata"," ",(0,t.jsx)(T.Tooltip,{title:"JSON object with additional information about this key. Used for tracking or custom logic",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"metadata",className:"mt-4",children:(0,t.jsx)(v.Input.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Tags"," ",(0,t.jsx)(T.Tooltip,{title:"Tags for tracking spend and/or doing tag-based routing. Used for analytics and filtering",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tags",className:"mt-4",help:"Tags for tracking spend and/or doing tag-based routing.",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",tokenSeparators:[","],options:eS})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"MCP Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(T.Tooltip,{title:"Select which MCP servers or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",help:"Select MCP servers or access groups this key can access",children:(0,t.jsx)(Y.default,{onChange:e=>eT.setFieldValue("allowed_mcp_servers_and_groups",e),value:eT.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:ep,teamId:eZ?.team_id??null,placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(j.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(j.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(Z.default,{accessToken:ep,selectedServers:(eT.getFieldValue("allowed_mcp_servers_and_groups")?.servers||[]).filter(e=>e!==X.NO_MCP_SERVERS_SENTINEL),toolPermissions:eT.getFieldValue("mcp_tool_permissions")||{},onChange:e=>eT.setFieldsValue({mcp_tool_permissions:e})})})})]})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Agent Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Agents"," ",(0,t.jsx)(T.Tooltip,{title:"Select which agents or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_agents_and_groups",help:"Select agents or access groups this key can access",children:(0,t.jsx)(M.default,{onChange:e=>eT.setFieldValue("allowed_agents_and_groups",e),value:eT.getFieldValue("allowed_agents_and_groups"),accessToken:ep,placeholder:"Select agents or access groups (optional)"})})})]}),ex?(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!0,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]}):(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Key-level logging settings is an enterprise feature, get in touch -",(0,t.jsx)("a",{href:"https://www.litellm.ai/enterprise",target:"_blank",children:"https://www.litellm.ai/enterprise"})]}),placement:"top",children:(0,t.jsxs)("div",{style:{position:"relative"},children:[(0,t.jsx)("div",{style:{opacity:.5},children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!1,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]})}),(0,t.jsx)("div",{style:{position:"absolute",inset:0,cursor:"not-allowed"}})]})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Router Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4 w-full",children:(0,t.jsx)(U.default,{accessToken:ep||"",value:ty||void 0,onChange:tf,modelData:eE.length>0?{data:eE.map(e=>({model_name:e}))}:void 0},tk)})})]},`router-settings-accordion-${tk}`),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Model Aliases"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)(y.Text,{className:"text-sm text-gray-600 mb-4",children:"Create custom aliases for models that can be used in API calls. This allows you to create shortcuts for specific models."}),(0,t.jsx)(R.default,{accessToken:ep,initialModelAliases:tu,onAliasUpdate:tm,showExampleConfig:!1})]})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Key Lifecycle"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)($.default,{form:eT,autoRotationEnabled:tp,onAutoRotationChange:tg,rotationInterval:th,onRotationIntervalChange:tx,isCreateMode:!0})})}),(0,t.jsx)(j.Form.Item,{name:"duration",hidden:!0,initialValue:null,children:(0,t.jsx)(v.Input,{})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("b",{children:"Advanced Settings"}),(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Learn more about advanced settings in our"," ",(0,t.jsx)("a",{href:et.proxyBaseUrl?`${et.proxyBaseUrl}/#/key%20management/generate_key_fn_key_generate_post`:"/#/key%20management/generate_key_fn_key_generate_post",target:"_blank",rel:"noopener noreferrer",className:"text-blue-400 hover:text-blue-300",children:"documentation"})]}),children:(0,t.jsx)(d.InfoCircleOutlined,{className:"text-gray-400 hover:text-gray-300 cursor-help"})})]})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(B.default,{schemaComponent:"GenerateKeyRequest",form:eT,excludedFields:["key_alias","team_id","organization_id","models","duration","metadata","tags","guardrails","max_budget","budget_duration","tpm_limit","rpm_limit",...ek?["key"]:[]]})})]})]})]})}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(_.Button,{htmlType:"submit",disabled:tM,style:{opacity:tM?.5:1},children:"Create Key"})})]})}),e5&&(0,t.jsx)(w.Modal,{title:"Create New User",open:e5,onCancel:()=>e6(!1),footer:null,width:800,children:(0,t.jsx)(W.CreateUserButton,{userID:eg,accessToken:ep,teams:eo,possibleUIRoles:e8,onUserCreated:e=>{e9(e),eT.setFieldsValue({user_id:e}),e6(!1)},isEmbedded:!0})}),eL&&(0,t.jsx)(w.Modal,{open:eI,onOk:tL,onCancel:tF,footer:null,children:(0,t.jsxs)(x.Grid,{numItems:1,className:"gap-2 w-full",children:[(0,t.jsx)(b.Title,{children:"Save your Key"}),(0,t.jsx)(h.Col,{numColSpan:1,children:null!=eL?(0,t.jsx)(es.default,{apiKey:eL}):(0,t.jsx)(y.Text,{children:"Key being created, this might take 30s"})})]})})]})},"fetchTeamModels",0,ei,"fetchUserModels",0,en],702597)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/00tczcrtv5upo.js b/litellm/proxy/_experimental/out/_next/static/chunks/00tczcrtv5upo.js new file mode 100644 index 00000000000..caf394915fd --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/00tczcrtv5upo.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,337822,e=>{"use strict";var t,n=e.i(843476);e.s([],158421),e.i(158421);var i=e.i(271645),r=e.i(956789),a=e.i(17989),o=e.i(46420);e.i(247167);var s=e.i(733332);let l=i.createContext(void 0);function u(e){let t=i.useContext(l);if(void 0===t&&!e)throw Error((0,s.default)(47));return t}var d=e.i(174080),c=e.i(301252),p=e.i(616269),f=e.i(439957),g=e.i(56434),h=e.i(264111),v=e.i(116786),m=e.i(990627),S=e.i(638396);let b={...v.popupStoreSelectors,disabled:(0,p.createSelector)(e=>e.disabled),instantType:(0,p.createSelector)(e=>e.instantType),openMethod:(0,p.createSelector)(e=>e.openMethod),openChangeReason:(0,p.createSelector)(e=>e.openChangeReason),modal:(0,p.createSelector)(e=>e.modal),focusManagerModal:(0,p.createSelector)(e=>e.focusManagerModal),stickIfOpen:(0,p.createSelector)(e=>e.stickIfOpen),titleElementId:(0,p.createSelector)(e=>e.titleElementId),descriptionElementId:(0,p.createSelector)(e=>e.descriptionElementId),openOnHover:(0,p.createSelector)(e=>e.openOnHover),closeDelay:(0,p.createSelector)(e=>e.closeDelay),hasViewport:(0,p.createSelector)(e=>e.hasViewport)};class R extends c.ReactStore{constructor(e,t,n=!1){const r={...(0,v.createInitialPopupStoreState)(),disabled:!1,modal:!1,focusManagerModal:!1,instantType:void 0,openMethod:null,openChangeReason:null,titleElementId:void 0,descriptionElementId:void 0,stickIfOpen:!0,nested:!1,openOnHover:!1,closeDelay:0,hasViewport:!1,...e},a=new m.PopupTriggerMap;r.open&&e?.mounted===void 0&&(r.mounted=!0),r.floatingRootContext=(0,v.createPopupFloatingRootContext)(a,t,n),super(r,{popupRef:i.createRef(),backdropRef:i.createRef(),internalBackdropRef:i.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerFocusTargetRef:i.createRef(),beforeContentFocusGuardRef:i.createRef(),stickIfOpenTimeout:new f.Timeout,triggerElements:a},b)}setOpen=(e,t)=>{let n=t.reason===g.REASONS.triggerHover,i=t.reason===g.REASONS.triggerPress&&0===t.event.detail,r=!e&&(t.reason===g.REASONS.escapeKey||null==t.reason),a=(0,h.attachPreventUnmountOnClose)(t),o=this.select("activeTriggerId");if(e||t.reason!==g.REASONS.closePress||null!=t.trigger||null==o||(t.trigger=this.context.triggerElements.getById(o)??this.select("activeTriggerElement")??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let s=()=>{let n={open:e,openChangeReason:t.reason};(0,h.setPopupOpenState)(n,e,t.trigger,a()),this.update(n)};n?(this.set("stickIfOpen",!0),this.context.stickIfOpenTimeout.start(S.PATIENT_CLICK_THRESHOLD,()=>{this.set("stickIfOpen",!1)}),d.flushSync(s)):s(),i||r?this.set("instantType",i?"click":"dismiss"):t.reason===g.REASONS.focusOut?this.set("instantType","focus"):this.set("instantType",void 0)};static useStore(e,t){let{store:n,internalStore:r}=(0,h.usePopupStore)(e,(e,n)=>new R(t,e,n));return i.useEffect(()=>r?.disposeEffect(),[r]),n}disposeEffect=()=>this.context.stickIfOpenTimeout.disposeEffect()}var C=e.i(675606),y=e.i(176782);function E({props:e}){let{children:t,open:r,defaultOpen:a=!1,onOpenChange:s,onOpenChangeComplete:u,modal:d=!1,handle:c,triggerId:p,defaultTriggerId:f=null}=e,v=R.useStore(c?.store,{modal:d,open:a,openProp:r,activeTriggerId:f,triggerIdProp:p});(0,h.useInitialOpenSync)(v,r,a,f),v.useControlledProp("openProp",r),v.useControlledProp("triggerIdProp",p);let m=v.useState("open"),S=v.useState("mounted"),b=v.useState("payload"),y=null!=(0,o.useFloatingParentNodeId)();v.useContextCallback("onOpenChange",s),v.useContextCallback("onOpenChangeComplete",u),(0,h.usePopupRootSync)(v,m),(0,h.useImplicitActiveTrigger)(v);let{forceUnmount:P}=(0,h.useOpenStateTransitions)(m,v,()=>{v.update({stickIfOpen:!0,openChangeReason:null})});v.useSyncedValues({modal:d,nested:y}),i.useEffect(()=>{m||v.context.stickIfOpenTimeout.clear()},[v,m]);let O=i.useCallback(()=>{v.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction))},[v]);i.useImperativeHandle(e.actionsRef,()=>({unmount:P,close:O}),[P,O]);let k=m||S,I=i.useMemo(()=>({store:v}),[v]);return(0,n.jsxs)(l.Provider,{value:I,children:[k&&(0,n.jsx)(x,{store:v,modal:d}),"function"==typeof t?t({payload:b}):t]})}function x({store:e,modal:t}){let n=e.useState("floatingRootContext"),o=(0,a.useDismiss)(n,{outsidePressEvent:{mouse:"trap-focus"===t?"sloppy":"intentional",touch:"sloppy"}}),s=o.reference??r.EMPTY_OBJECT,l=o.trigger??r.EMPTY_OBJECT,u=i.useMemo(()=>(0,y.mergeProps)(h.FOCUSABLE_POPUP_PROPS,o.floating),[o.floating]);return(0,h.usePopupInteractionProps)(e,{activeTriggerProps:s,inactiveTriggerProps:l,popupProps:u}),null}var P=e.i(540886),O=e.i(405005),k=e.i(552245),I=e.i(650316),w=e.i(385689),T=e.i(872135),M=e.i(788015),A=e.i(152535),F=e.i(346570),j=e.i(32199);let N=i.forwardRef(function(e,t){let{render:r,className:a,style:o,disabled:l=!1,nativeButton:d=!0,handle:c,payload:p,openOnHover:f=!1,delay:v=300,closeDelay:m=0,id:b,...R}=e,C=u(!0),y=c?.store??C?.store;if(!y)throw Error((0,s.default)(74));let E=(0,M.useBaseUiId)(b),x=y.useState("isTriggerActive",E),N=y.useState("floatingRootContext"),D=y.useState("isOpenedByTrigger",E),B=y.useState("triggerPopupId",E),H=i.useRef(null),{registerTrigger:L,isMountedByThisTrigger:V}=(0,h.useTriggerDataForwarding)(E,H,y,{payload:p,disabled:l,openOnHover:f,closeDelay:m}),z=y.useState("openChangeReason"),U=y.useState("stickIfOpen"),K=y.useState("openMethod"),_=y.useState("focusManagerModal"),W=(0,T.useHoverReferenceInteraction)(N,{enabled:!l&&null!=N&&f&&("touch"!==K||z!==g.REASONS.triggerPress),mouseOnly:!0,move:!1,handleClose:(0,I.safePolygon)(),restMs:v,delay:{close:m},triggerElementRef:H,isActiveTrigger:x,isClosing:()=>"ending"===y.select("transitionStatus")}),G=(0,w.useClick)(N,{enabled:null!=N,stickIfOpen:U}),q=(0,j.useOpenMethodTriggerProps)(()=>y.select("open"),e=>{y.set("openMethod",e)}),Y=y.useState("triggerProps",V),{getButtonProps:J,buttonRef:$}=(0,P.useButton)({disabled:l,native:d}),{preFocusGuardRef:Q,handlePreFocusGuardFocus:X,handleFocusTargetFocus:Z}=(0,F.useTriggerFocusGuards)(y,H),ee=(0,k.useRenderElement)("button",e,{state:{disabled:l,open:D},ref:[$,t,L,H],props:[G.reference,W,Y,q,{[S.CLICK_TRIGGER_IDENTIFIER]:"",id:E,"aria-haspopup":"dialog","aria-expanded":D,"aria-controls":B},R,J],stateAttributesMapping:{open:e=>e&&z===g.REASONS.triggerPress?O.pressableTriggerOpenStateMapping.open(e):O.triggerOpenStateMapping.open(e)}});return V&&!_?(0,n.jsxs)(i.Fragment,{children:[(0,n.jsx)(A.FocusGuard,{ref:Q,onFocus:X}),(0,n.jsx)(i.Fragment,{children:ee},E),(0,n.jsx)(A.FocusGuard,{ref:y.context.triggerFocusTargetRef,onFocus:Z})]}):(0,n.jsx)(i.Fragment,{children:ee},E)});var D=e.i(726674);let B=i.createContext(void 0),H=i.forwardRef(function(e,t){let{keepMounted:i=!1,...r}=e,{store:a}=u();return a.useState("mounted")||i?(0,n.jsx)(B.Provider,{value:i,children:(0,n.jsx)(D.FloatingPortal,{ref:t,...r})}):null});var L=e.i(144394),V=e.i(146376);let z=i.createContext(void 0);function U(){let e=i.useContext(z);if(!e)throw Error((0,s.default)(46));return e}var K=e.i(329365),_=e.i(426),W=e.i(222640),G=e.i(360495),q=e.i(789579),Y=e.i(33383);let J=i.forwardRef(function(e,t){let{render:r,className:a,style:l,anchor:d,positionMethod:c="absolute",side:p="bottom",align:f="center",sideOffset:h=0,alignOffset:v=0,collisionBoundary:m="clipping-ancestors",collisionPadding:b=5,arrowPadding:R=5,sticky:C=!1,disableAnchorTracking:y=!1,collisionAvoidance:E=S.POPUP_COLLISION_AVOIDANCE,...x}=e,{store:P}=u(),O=function(){let e=i.useContext(B);if(void 0===e)throw Error((0,s.default)(45));return e}(),k=(0,o.useFloatingNodeId)(),I=P.useState("floatingRootContext"),w=P.useState("mounted"),T=P.useState("open"),M=P.useState("openChangeReason"),A=P.useState("activeTriggerElement"),F=P.useState("modal"),j=P.useState("openMethod"),N=P.useState("positionerElement"),D=P.useState("instantType"),H=P.useState("transitionStatus"),U=P.useState("hasViewport"),J=i.useRef(null),$=(0,W.useAnimationsFinished)(N,!1,!1),Q=(0,K.useAnchorPositioning)({anchor:d,floatingRootContext:I,positionMethod:c,mounted:w,side:p,sideOffset:h,align:f,alignOffset:v,arrowPadding:R,collisionBoundary:m,collisionPadding:b,sticky:C,disableAnchorTracking:y,keepMounted:O,nodeId:k,collisionAvoidance:E,adaptiveOrigin:U?G.adaptiveOrigin:void 0}),X=I.useState("domReferenceElement");(0,V.useIsoLayoutEffect)(()=>{let e=J.current;if(X&&(J.current=X),e&&X&&X!==e){P.set("instantType",void 0);let e=new AbortController;return $(()=>{P.set("instantType","trigger-change")},e.signal),()=>{e.abort()}}},[X,$,P]),(0,Y.useAnchoredPopupScrollLock)(T&&!0===F&&M!==g.REASONS.triggerHover,"touch"===j,N,A);let Z=i.useCallback(e=>{P.set("positionerElement",e)},[P]),ee={open:T,side:Q.side,align:Q.align,anchorHidden:Q.anchorHidden,instant:D},et=(0,q.usePositioner)(e,ee,{styles:Q.positionerStyles,transitionStatus:H,props:x,refs:[t,Z],hidden:!w,inert:!T});return(0,n.jsxs)(z.Provider,{value:Q,children:[w&&!0===F&&M!==g.REASONS.triggerHover&&(0,n.jsx)(_.InternalBackdrop,{ref:P.context.internalBackdropRef,inert:(0,L.inertValue)(!T),cutout:A}),(0,n.jsx)(o.FloatingNode,{id:k,children:et})]})});var $=e.i(229315),Q=e.i(61487),X=e.i(431157),Z=e.i(209407),ee=e.i(137584),et=e.i(673327),en=e.i(96533),ei=e.i(815982),er=e.i(667865);let ea=i.createContext(void 0);function eo(e){let{value:t,children:i}=e;return(0,n.jsx)(ea.Provider,{value:t,children:i})}let es={...O.popupStateMapping,...Z.transitionStatusMapping},el=i.forwardRef(function(e,t){let{render:r,className:a,style:o,initialFocus:s,finalFocus:l,...d}=e,{store:c}=u(),p=U(),f=null!=(0,en.useToolbarRootContext)(!0),{context:v,hasClosePart:m}=function(){let[e,t]=i.useState(0),n=(0,er.useStableCallback)(()=>(t(e=>e+1),()=>{t(e=>Math.max(0,e-1))}));return{context:i.useMemo(()=>({register:n}),[n]),hasClosePart:e>0}}(),S=c.useState("open"),b=c.useState("openMethod"),R=c.useState("instantType"),C=c.useState("transitionStatus"),y=c.useState("popupProps"),E=c.useState("titleElementId"),x=c.useState("descriptionElementId"),P=c.useState("modal"),O=c.useState("mounted"),I=c.useState("openChangeReason"),w=c.useState("activeTriggerElement"),T=c.useState("floatingRootContext"),M=T.useState("floatingId"),A=c.useState("disabled"),F=c.useState("openOnHover"),j=c.useState("closeDelay"),N=d.id??M;(0,ee.useOpenChangeComplete)({open:S,ref:c.context.popupRef,onComplete(){S&&c.context.onOpenChangeComplete?.(!0)}}),(0,X.useHoverFloatingInteraction)(T,{enabled:F&&!A,closeDelay:j});let D=void 0===s?(0,h.createDefaultInitialFocus)(c.context.popupRef):s,B=!1!==P&&m;c.useSyncedValue("focusManagerModal",B);let H=i.useCallback(e=>{c.set("popupElement",e)},[c]),L={open:S,side:p.side,align:p.align,instant:R,transitionStatus:C},V=(0,k.useRenderElement)("div",e,{state:L,ref:[t,c.context.popupRef,H],props:[y,{id:N,role:"dialog",...h.FOCUSABLE_POPUP_PROPS,"aria-labelledby":E,"aria-describedby":x,onKeyDown(e){f&&et.COMPOSITE_KEYS.has(e.key)&&e.stopPropagation()}},(0,ei.getDisabledMountTransitionStyles)(C),d],stateAttributesMapping:es});return(0,n.jsx)(Q.FloatingFocusManager,{context:T,openInteractionType:b,modal:B,disabled:!O||I===g.REASONS.triggerHover,initialFocus:D,returnFocus:l,restoreFocus:"popup",previousFocusableElement:(0,$.isHTMLElement)(w)?w:void 0,nextFocusableElement:c.context.triggerFocusTargetRef,beforeContentFocusGuardRef:c.context.beforeContentFocusGuardRef,children:(0,n.jsx)(eo,{value:v,children:V})})}),eu=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=o.useState("open"),{arrowRef:l,side:d,align:c,arrowUncentered:p,arrowStyles:f}=U();return(0,k.useRenderElement)("div",e,{state:{open:s,side:d,align:c,uncentered:p},ref:[t,l],props:[{style:f,"aria-hidden":!0},a],stateAttributesMapping:O.popupStateMapping})}),ed={...O.popupStateMapping,...Z.transitionStatusMapping},ec=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=o.useState("open"),l=o.useState("mounted"),d=o.useState("transitionStatus"),c=o.useState("openChangeReason");return(0,k.useRenderElement)("div",e,{state:{open:s,transitionStatus:d},ref:[o.context.backdropRef,t],props:[{role:"presentation",hidden:!l,style:{pointerEvents:c===g.REASONS.triggerHover?"none":void 0,userSelect:"none",WebkitUserSelect:"none"}},a],stateAttributesMapping:ed})}),ep=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=(0,M.useBaseUiId)(a.id);return o.useSyncedValueWithCleanup("titleElementId",s),(0,k.useRenderElement)("h2",e,{ref:t,props:[{id:s},a]})}),ef=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=(0,M.useBaseUiId)(a.id);return o.useSyncedValueWithCleanup("descriptionElementId",s),(0,k.useRenderElement)("p",e,{ref:t,props:[{id:s},a]})}),eg=i.forwardRef(function(e,t){let n,{render:r,className:a,style:o,disabled:s=!1,nativeButton:l=!0,...d}=e,{buttonRef:c,getButtonProps:p}=(0,P.useButton)({disabled:s,focusableWhenDisabled:!1,native:l}),{store:f}=u();return n=i.useContext(ea),(0,V.useIsoLayoutEffect)(()=>n?.register(),[n]),(0,k.useRenderElement)("button",e,{ref:[t,c],props:[{onClick(e){f.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.closePress,e.nativeEvent))}},d,p]})}),eh=((t={}).popupWidth="--popup-width",t.popupHeight="--popup-height",t);var ev=e.i(818390);let em={activationDirection:e=>e?{"data-activation-direction":e}:null},eS=i.forwardRef(function(e,t){let{render:n,className:i,style:r,children:a,...o}=e,{store:s}=u(),{side:l}=U(),d=s.useState("instantType"),{children:c,state:p}=(0,ev.usePopupViewport)({store:s,side:l,cssVars:eh,children:a}),f={activationDirection:p.activationDirection,transitioning:p.transitioning,instant:d};return(0,k.useRenderElement)("div",e,{state:f,ref:t,props:[o,{children:c}],stateAttributesMapping:em})});class eb{constructor(){this.store=new R}open(e){let t=e?this.store.context.triggerElements.getById(e)??void 0:void 0;if(e&&!t)throw Error((0,s.default)(80,e));this.store.setOpen(!0,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction,void 0,t))}close(){this.store.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction,void 0,void 0))}get isOpen(){return this.store.select("open")}}e.s(["Arrow",0,eu,"Backdrop",0,ec,"Close",0,eg,"Description",0,ef,"Handle",0,eb,"Popup",0,el,"Portal",0,H,"Positioner",0,J,"Root",0,function(e){return u(!0)?(0,n.jsx)(E,{props:e}):(0,n.jsx)(o.FloatingTree,{children:(0,n.jsx)(E,{props:e})})},"Title",0,ep,"Trigger",0,N,"Viewport",0,eS,"createHandle",0,function(){return new eb}],466914);var eR=e.i(466914),eR=eR,eC=e.i(115504);e.s(["Popover",0,function({...e}){return(0,n.jsx)(eR.Root,{"data-slot":"popover",...e})},"PopoverContent",0,function({className:e,align:t="center",alignOffset:i=0,side:r="bottom",sideOffset:a=4,...o}){return(0,n.jsx)(eR.Portal,{children:(0,n.jsx)(eR.Positioner,{align:t,alignOffset:i,side:r,sideOffset:a,className:"isolate z-50",children:(0,n.jsx)(eR.Popup,{"data-slot":"popover-content",className:(0,eC.cn)("z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...o})})})},"PopoverTrigger",0,function({...e}){return(0,n.jsx)(eR.Trigger,{"data-slot":"popover-trigger",...e})}],337822)},204258,e=>{"use strict";var t,n,i,r=e.i(843476);e.s([],958842),e.i(958842),e.i(247167);var a=e.i(271645),o=e.i(667865),s=e.i(552245),l=e.i(951437),u=e.i(788015),d=e.i(675606),c=e.i(56434),p=e.i(223910),f=e.i(733332);let g=a.createContext(void 0);function h(){let e=a.useContext(g);if(void 0===e)throw Error((0,f.default)(15));return e}var v=e.i(209407);let m=((t={}).open="data-open",t.closed="data-closed",t[t.startingStyle=v.TransitionStatusDataAttributes.startingStyle]="startingStyle",t[t.endingStyle=v.TransitionStatusDataAttributes.endingStyle]="endingStyle",t),S=((n={}).panelOpen="data-panel-open",n),b={[m.open]:""},R={[m.closed]:""},C={open:e=>e?b:R,...v.transitionStatusMapping},y=a.forwardRef(function(e,t){let{render:n,className:i,defaultOpen:f=!1,disabled:h=!1,onOpenChange:v,open:m,style:S,...b}=e,R=(0,o.useStableCallback)(v),y=function(e){let{open:t,defaultOpen:n,onOpenChange:i,disabled:r}=e,[s,f]=(0,l.useControlled)({controlled:t,default:n,name:"Collapsible",state:"open"}),{mounted:g,setMounted:h,transitionStatus:v}=(0,p.useTransitionStatus)(s,!0,!0),m=(0,u.useBaseUiId)(),[S,b]=a.useState(),R=S??m,C=(0,o.useStableCallback)(e=>{let t=!s,n=(0,d.createChangeEventDetails)(c.REASONS.triggerPress,e.nativeEvent);i(t,n),n.isCanceled||f(t)});return a.useMemo(()=>({disabled:r,handleTrigger:C,mounted:g,open:s,panelId:R,setMounted:h,setOpen:f,setPanelIdState:b,transitionStatus:v}),[r,C,g,s,R,h,f,b,v])}({open:m,defaultOpen:f,onOpenChange:R,disabled:h}),E=a.useMemo(()=>({open:y.open,disabled:y.disabled,transitionStatus:y.transitionStatus}),[y.open,y.disabled,y.transitionStatus]),x=a.useMemo(()=>({...y,onOpenChange:R,state:E}),[y,R,E]),P=(0,s.useRenderElement)("div",e,{state:E,ref:t,props:b,stateAttributesMapping:C});return(0,r.jsx)(g.Provider,{value:x,children:P})});var E=e.i(540886);let x={open:e=>e?{[S.panelOpen]:""}:null,...v.transitionStatusMapping},P=a.forwardRef(function(e,t){let{panelId:n,open:i,handleTrigger:r,state:a,disabled:o}=h(),{className:l,disabled:u=o,render:d,nativeButton:c=!0,style:p,...f}=e,{getButtonProps:g,buttonRef:v}=(0,E.useButton)({disabled:u,focusableWhenDisabled:!0,native:c});return(0,s.useRenderElement)("button",e,{state:a,ref:[t,v],props:[{"aria-controls":i?n:void 0,"aria-expanded":i,onClick:r},f,g],stateAttributesMapping:x})});var O=e.i(146376),k=e.i(377570),I=e.i(574735),w=e.i(828918),T=e.i(708445),M=e.i(446265),A=e.i(333848),F=e.i(137584),j=e.i(222640);let N={height:void 0,width:void 0};function D(e){return{height:e.scrollHeight,width:e.scrollWidth}}function B(e){return e.split(",").map(e=>e.trim()).some(e=>""!==e&&Number.parseFloat(e)>0)}function H(e,t,n){let i=e.style.getPropertyValue(t),r=e.style.getPropertyPriority(t);return e.style.setProperty(t,n),()=>{""===i?e.style.removeProperty(t):e.style.setProperty(t,i,r)}}let L=((i={}).collapsiblePanelHeight="--collapsible-panel-height",i.collapsiblePanelWidth="--collapsible-panel-width",i),V=a.forwardRef(function(e,t){let{className:n,hiddenUntilFound:i,keepMounted:r,render:l,id:u,style:p,...f}=e,{mounted:g,onOpenChange:v,open:S,panelId:b,setMounted:R,setPanelIdState:y,setOpen:E,state:x,transitionStatus:P}=h();(0,O.useIsoLayoutEffect)(()=>{if(u)return y(u),()=>{y(void 0)}},[u,y]);let{height:V,props:z,ref:U,shouldPreventOpenAnimation:K,shouldRender:_,transitionStatus:W,width:G}=function(e){let{externalRef:t,hiddenUntilFound:n,id:i,keepMounted:r,mounted:s,onOpenChange:l,open:u,setMounted:p,setOpen:f,transitionStatus:g}=e,h=a.useRef(null),v=a.useRef(null),[S,b]=a.useState(N),R=a.useRef(N),C=a.useRef(!1),y=a.useRef(u),E=a.useRef(!1),[x,P]=a.useState(!1),k=a.useRef(null),L=(0,w.useMergedRefs)(t,h),V=(0,M.useValueAsRef)({mounted:s,open:u}),z=(0,j.useAnimationsFinished)(h,!1,!1),U=!u&&!s,K=x?"idle":g,_=u&&(y.current||E.current),W=!u&&s&&"css-animation"===v.current&&void 0===S.height&&void 0===S.width?R.current:S,G=n&&U&&"css-animation"!==v.current,q=(0,o.useStableCallback)((e,t=!0)=>{t&&(R.current=e),b(e)}),Y=(0,o.useStableCallback)(()=>{k.current?.(),k.current=null}),J=(0,o.useStableCallback)(e=>{Y(),k.current=()=>{k.current=null,e()}}),$=(0,o.useStableCallback)(()=>{u&&s&&"css-animation"===v.current&&(E.current=!0)});(0,O.useIsoLayoutEffect)(()=>{x&&"starting"!==g&&P(!1)},[x,g]),a.useEffect(()=>()=>{$(),Y()},[$,Y]),(0,O.useIsoLayoutEffect)(()=>{let e=h.current;if(!e)return;!u&&k.current&&Y();let t=function(e,t=!1){let n=(0,A.ownerWindow)(e).getComputedStyle(e),i=(n.animationName.split(",").map(e=>e.trim()).some(e=>""!==e&&"none"!==e)||t)&&B(n.animationDuration),r=B(n.transitionDuration);return i&&r||r?"css-transition":i?"css-animation":"none"}(e,_);if(v.current=t,u&&"idle"===g&&y.current&&"css-animation"===t){R.current=D(e);return}if(u&&"starting"===g){let n=C.current;if(C.current=!1,"none"===t){q(D(e)),P(!0);return}if("css-transition"===t){let t=function(e){let t={"justify-content":e.style.justifyContent,"align-items":e.style.alignItems,"align-content":e.style.alignContent,"justify-items":e.style.justifyItems};function n(){Object.entries(t).forEach(([t,n])=>{""===n?e.style.removeProperty(t):e.style.setProperty(t,n)})}Object.keys(t).forEach(t=>{e.style.setProperty(t,"initial","important")});let i=T.AnimationFrame.request(n);return()=>{T.AnimationFrame.cancel(i),n()}}(e);return q(D(e)),n&&(J(H(e,"transition-duration","0s")),P(!0)),t}if("css-animation"===t){if(q(D(e)),!n)return void H(e,"animation-name","none")();let t=H(e,"animation-name","none"),i=H(e,"animation-duration","0s");return t(),J(i),P(!0),void 0}}if(!u&&s&&("idle"===g||"starting"===g)){if(y.current=!1,E.current=!1,"none"===t){q(N,!1),p(!1);return}q(D(e));return}if("ending"!==g)return;if("none"===t)return void p(!1);let n=D(e);(n.height??0)>0||(n.width??0)>0?(q(n),"css-animation"===t&&H(e,"animation-name","none")()):p(!1)},[s,u,Y,q,p,J,_,g]),(0,F.useOpenChangeComplete)({enabled:u&&s&&"idle"===K,open:!0,ref:h,onComplete(){u&&q(N,!1)}}),a.useEffect(()=>{if(u||!s||"ending"!==K||!h.current)return;let e=new AbortController,t=-1;function n(){V.current.open||(p(!1),q(N,!1))}return t=T.AnimationFrame.request(()=>{e.signal.aborted||z(n,e.signal)}),()=>{T.AnimationFrame.cancel(t),e.abort()}},[V,s,u,K,z,q,p]),(0,O.useIsoLayoutEffect)(()=>{let e=h.current;e&&n&&U&&e.setAttribute("hidden","until-found")},[U,n]),a.useEffect(function(){let e=h.current;if(e)return(0,I.addEventListener)(e,"beforematch",function(e){let t=(0,d.createChangeEventDetails)(c.REASONS.none,e);l(!0,t),t.isCanceled||(C.current=!0,f(!0))})},[l,f]);let Q=r||n||s||u;return{height:W.height,props:{...G?{[m.startingStyle]:""}:void 0,hidden:U,id:i},ref:L,shouldPreventOpenAnimation:_,shouldRender:Q,transitionStatus:K,width:W.width}}({externalRef:t,hiddenUntilFound:i??!1,id:b,keepMounted:r??!1,mounted:g,onOpenChange:v,open:S,setMounted:R,setOpen:E,transitionStatus:P}),q={...x,transitionStatus:W},Y=(0,k.resolveStyle)(p,q),J=(0,s.useRenderElement)("div",{...e,style:void 0},{state:q,ref:U,props:[z,{style:{[L.collapsiblePanelHeight]:void 0===V?"auto":`${V}px`,[L.collapsiblePanelWidth]:void 0===G?"auto":`${G}px`}},f,Y?{style:Y}:void 0,K?{style:{animationName:"none"}}:void 0],stateAttributesMapping:C});return _?J:null});e.s(["Panel",0,V,"Root",0,y,"Trigger",0,P],596315);var z=e.i(596315),z=z;e.s(["Collapsible",0,function({...e}){return(0,r.jsx)(z.Root,{"data-slot":"collapsible",...e})},"CollapsibleContent",0,function({...e}){return(0,r.jsx)(z.Panel,{"data-slot":"collapsible-content",...e})},"CollapsibleTrigger",0,function({...e}){return(0,r.jsx)(z.Trigger,{"data-slot":"collapsible-trigger",...e})}],204258)},699375,e=>{"use strict";var t,n=e.i(843476);e.s([],924305),e.i(924305),e.i(247167);var i=e.i(271645),r=e.i(951437),a=e.i(828918),o=e.i(146376),s=e.i(502077),l=e.i(956789),u=e.i(333848),d=e.i(552245),c=e.i(176782),p=e.i(788015),f=e.i(540886),g=e.i(733332);let h=i.createContext(void 0);var v=e.i(875812);let m=((t={}).checked="data-checked",t.unchecked="data-unchecked",t.disabled="data-disabled",t.readonly="data-readonly",t.required="data-required",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),S={...v.fieldValidityMapping,checked:e=>e?{[m.checked]:""}:{[m.unchecked]:""}};var b=e.i(469690),R=e.i(381104),C=e.i(884708),y=e.i(247778),E=e.i(538489),x=e.i(675606),P=e.i(56434),O=e.i(606039);let k=i.forwardRef(function(e,t){let{checked:g,className:v,defaultChecked:m,"aria-labelledby":k,form:I,id:w,inputRef:T,name:M,nativeButton:A=!1,onCheckedChange:F,readOnly:j=!1,required:N=!1,disabled:D=!1,render:B,uncheckedValue:H,value:L,style:V,...z}=e,{clearErrors:U}=(0,C.useFormContext)(),{state:K,setTouched:_,setDirty:W,validityData:G,setFilled:q,setFocused:Y,validationMode:J,disabled:$,name:Q,validation:X}=(0,b.useFieldRootContext)(),{labelId:Z}=(0,y.useLabelableContext)(),ee=$||D,et=Q??M,en=i.useRef(null),ei=(0,a.useMergedRefs)(en,T,X.inputRef),er=i.useRef(null),ea=(0,p.useBaseUiId)(),eo=(0,E.useLabelableId)({id:w,implicit:!1,controlRef:er}),es=A?void 0:eo,[el,eu]=(0,r.useControlled)({controlled:g,default:!!m,name:"Switch",state:"checked"});(0,R.useRegisterFieldControl)(er,ea,el,void 0,!ee,M),(0,o.useIsoLayoutEffect)(()=>{en.current&&q(en.current.checked)},[en,q]),(0,O.useValueChanged)(el,()=>{U(et),W(el!==G.initialValue),q(el),X.change(el)});let{getButtonProps:ed,buttonRef:ec}=(0,f.useButton)({disabled:ee,native:A}),ep=function(e,t,n,r=!0,a){let[s,l]=i.useState(),u=(0,p.useBaseUiId)(a?`${a}-label`:void 0),d=e??t??s;return(0,o.useIsoLayoutEffect)(()=>{let i=e||t||!r?void 0:function(e,t){let n=function(e){if(!e)return;let t=e.parentElement;if(t&&"LABEL"===t.tagName)return t;let n=e.id;if(n){let t=e.nextElementSibling;if(t&&t.htmlFor===n)return t}let i=e.labels;return i&&i[0]}(e);if(n)return!n.id&&t&&(n.id=t),n.id||void 0}(n.current,u);s!==i&&l(i)}),d}(k,Z,en,!A,es),ef=(0,c.mergeProps)({checked:el,disabled:ee,form:I,id:es,name:et,required:N,style:et?s.visuallyHiddenInput:s.visuallyHidden,tabIndex:-1,type:"checkbox","aria-hidden":!0,ref:ei,onChange(e){if(e.nativeEvent.defaultPrevented)return;if(j)return void e.preventDefault();let t=e.currentTarget.checked,n=(0,x.createChangeEventDetails)(P.REASONS.none,e.nativeEvent);F?.(t,n),n.isCanceled||eu(t)},onFocus(){er.current?.focus()}},e=>X.getValidationProps(ee,e),void 0!==L?{value:L}:l.EMPTY_OBJECT),eg=i.useMemo(()=>({...K,checked:el,disabled:ee,readOnly:j,required:N}),[K,el,ee,j,N]),eh=(0,d.useRenderElement)("span",e,{state:eg,ref:[t,er,ec],props:[{id:A?eo:ea,role:"switch","aria-checked":el,"aria-readonly":j||void 0,"aria-required":N||void 0,"aria-labelledby":ep,onFocus(){ee||Y(!0)},onBlur(){let e=en.current;e&&!ee&&(_(!0),Y(!1),"onBlur"===J&&X.commit(e.checked))},onClick(e){if(j||ee)return;e.preventDefault();let t=en.current;t&&t.dispatchEvent(new((0,u.ownerWindow)(t)).PointerEvent("click",{bubbles:!0,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,altKey:e.altKey,metaKey:e.metaKey}))}},z,ed,e=>X.getValidationProps(ee,e)],stateAttributesMapping:S});return(0,n.jsxs)(h.Provider,{value:eg,children:[eh,!el&&et&&void 0!==H&&(0,n.jsx)("input",{type:"hidden",form:I,name:et,value:H,disabled:ee}),(0,n.jsx)("input",{...ef,suppressHydrationWarning:!0})]})}),I=i.forwardRef(function(e,t){let{render:n,className:r,style:a,...o}=e,s=function(){let e=i.useContext(h);if(void 0===e)throw Error((0,g.default)(63));return e}();return(0,d.useRenderElement)("span",e,{state:s,ref:t,stateAttributesMapping:S,props:o})});e.s(["Root",0,k,"Thumb",0,I],450994);var w=e.i(450994),w=w,T=e.i(115504);e.s(["Switch",0,function({className:e,size:t="default",...i}){return(0,n.jsx)(w.Root,{"data-slot":"switch","data-size":t,className:(0,T.cn)("peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",e),...i,children:(0,n.jsx)(w.Thumb,{"data-slot":"switch-thumb",className:"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"})})}],699375)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/010e8lif45kbo.js b/litellm/proxy/_experimental/out/_next/static/chunks/010e8lif45kbo.js deleted file mode 100644 index 6cdee9105cd..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/010e8lif45kbo.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,362024,e=>{"use strict";var r=e.i(988122);e.s(["Collapse",()=>r.default])},21548,e=>{"use strict";var r=e.i(616303);e.s(["Empty",()=>r.default])},166406,e=>{"use strict";var r=e.i(190144);e.s(["CopyOutlined",()=>r.default])},599724,936325,e=>{"use strict";var r=e.i(95779),t=e.i(444755),a=e.i(673706),o=e.i(271645);let l=o.default.forwardRef((e,l)=>{let{color:s,className:n,children:i}=e;return o.default.createElement("p",{ref:l,className:(0,t.tremorTwMerge)("text-tremor-default",s?(0,a.getColorClassNames)(s,r.colorPalette.text).textColor:(0,t.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),n)},i)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},994388,e=>{"use strict";var r=e.i(290571),t=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,n=(e,r,t,a,o)=>{clearTimeout(a.current);let s=l(e);r(s),t.current=s,o&&o({current:s})};var i=e.i(480731),d=e.i(444755),c=e.i(673706);let m=e=>{var t=(0,r.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var u=e.i(95779);let f={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},g=(e,r)=>{switch(e){case"primary":return{textColor:r?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:r?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,c.getColorClassNames)(r,u.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:r?(0,c.getColorClassNames)(r,u.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:r?(0,c.getColorClassNames)(r,u.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:r?(0,c.getColorClassNames)(r,u.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:r?(0,c.getColorClassNames)(r,u.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:r?(0,c.getColorClassNames)(r,u.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:r?(0,d.tremorTwMerge)((0,c.getColorClassNames)(r,u.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:r?(0,c.getColorClassNames)(r,u.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:r?(0,c.getColorClassNames)(r,u.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:r?(0,c.getColorClassNames)(r,u.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},p=(0,c.makeClassName)("Button"),b=({loading:e,iconSize:r,iconPosition:t,Icon:o,needMargin:l,transitionStatus:s})=>{let n=l?t===i.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),u={default:c,entering:c,entered:r,exiting:r,exited:c};return e?a.default.createElement(m,{className:(0,d.tremorTwMerge)(p("icon"),"animate-spin shrink-0",n,u.default,u[s]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(p("icon"),"shrink-0",r,n)})},h=a.default.forwardRef((e,o)=>{let{icon:m,iconPosition:u=i.HorizontalPositions.Left,size:h=i.Sizes.SM,color:y,variant:v="primary",disabled:x,loading:C=!1,loadingText:w,children:k,tooltip:N,className:T}=e,O=(0,r.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),E=C||x,M=void 0!==m||C,j=C&&w,P=!(!k&&!j),S=(0,d.tremorTwMerge)(f[h].height,f[h].width),_="light"!==v?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",z=g(v,y),R=("light"!==v?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[h],{tooltipProps:B,getReferenceProps:$}=(0,t.useTooltip)(300),[L,H]=(({enter:e=!0,exit:r=!0,preEnter:t,preExit:o,timeout:i,initialEntered:d,mountOnEnter:c,unmountOnExit:m,onStateChange:u}={})=>{let[f,g]=(0,a.useState)(()=>l(d?2:s(c))),p=(0,a.useRef)(f),b=(0,a.useRef)(0),[h,y]="object"==typeof i?[i.enter,i.exit]:[i,i],v=(0,a.useCallback)(()=>{let e=((e,r)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(r)}})(p.current._s,m);e&&n(e,g,p,b,u)},[u,m]);return[f,(0,a.useCallback)(a=>{let l=e=>{switch(n(e,g,p,b,u),e){case 1:h>=0&&(b.current=((...e)=>setTimeout(...e))(v,h));break;case 4:y>=0&&(b.current=((...e)=>setTimeout(...e))(v,y));break;case 0:case 3:b.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},i=p.current.isEnter;"boolean"!=typeof a&&(a=!i),a?i||l(e?+!t:2):i&&l(r?o?3:4:s(m))},[v,u,e,r,t,o,h,y,m]),v]})({timeout:50});return(0,a.useEffect)(()=>{H(C)},[C]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([o,B.refs.setReference]),className:(0,d.tremorTwMerge)(p("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",_,R.paddingX,R.paddingY,R.fontSize,z.textColor,z.bgColor,z.borderColor,z.hoverBorderColor,E?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(g(v,y).hoverTextColor,g(v,y).hoverBgColor,g(v,y).hoverBorderColor),T),disabled:E},$,O),a.default.createElement(t.default,Object.assign({text:N},B)),M&&u!==i.HorizontalPositions.Right?a.default.createElement(b,{loading:C,iconSize:S,iconPosition:u,Icon:m,transitionStatus:L.status,needMargin:P}):null,j||k?a.default.createElement("span",{className:(0,d.tremorTwMerge)(p("text"),"text-tremor-default whitespace-nowrap")},j?w:k):null,M&&u===i.HorizontalPositions.Right?a.default.createElement(b,{loading:C,iconSize:S,iconPosition:u,Icon:m,transitionStatus:L.status,needMargin:P}):null)});h.displayName="Button",e.s(["Button",0,h],994388)},304967,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(480731),o=e.i(95779),l=e.i(444755),s=e.i(673706);let n=(0,s.makeClassName)("Card"),i=t.default.forwardRef((e,i)=>{let{decoration:d="",decorationColor:c,children:m,className:u}=e,f=(0,r.__rest)(e,["decoration","decorationColor","children","className"]);return t.default.createElement("div",Object.assign({ref:i,className:(0,l.tremorTwMerge)(n("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,s.getColorClassNames)(c,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),u)},f),m)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var r=e.i(290571),t=e.i(95779),a=e.i(444755),o=e.i(673706),l=e.i(271645);let s=l.default.forwardRef((e,s)=>{let{color:n,children:i,className:d}=e,c=(0,r.__rest)(e,["color","children","className"]);return l.default.createElement("p",Object.assign({ref:s,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",n?(0,o.getColorClassNames)(n,t.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),i)});s.displayName="Title",e.s(["Title",0,s],629569)},653496,e=>{"use strict";var r=e.i(721369);e.s(["Tabs",()=>r.default])},637235,e=>{"use strict";e.i(247167);var r=e.i(931067),t=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"}}]},name:"clock-circle",theme:"outlined"};var o=e.i(9583),l=t.forwardRef(function(e,l){return t.createElement(o.default,(0,r.default)({},e,{ref:l,icon:a}))});e.s(["ClockCircleOutlined",0,l],637235)},525720,e=>{"use strict";e.i(247167);var r=e.i(271645),t=e.i(343794),a=e.i(529681),o=e.i(908286),l=e.i(242064),s=e.i(246422),n=e.i(838378);let i=["wrap","nowrap","wrap-reverse"],d=["flex-start","flex-end","start","end","center","space-between","space-around","space-evenly","stretch","normal","left","right"],c=["center","start","end","flex-start","flex-end","self-start","self-end","baseline","normal","stretch"],m=function(e,r){let a,o,l;return(0,t.default)(Object.assign(Object.assign(Object.assign({},(a=!0===r.wrap?"wrap":r.wrap,{[`${e}-wrap-${a}`]:a&&i.includes(a)})),(o={},c.forEach(t=>{o[`${e}-align-${t}`]=r.align===t}),o[`${e}-align-stretch`]=!r.align&&!!r.vertical,o)),(l={},d.forEach(t=>{l[`${e}-justify-${t}`]=r.justify===t}),l)))},u=(0,s.genStyleHooks)("Flex",e=>{let{paddingXS:r,padding:t,paddingLG:a}=e,o=(0,n.mergeToken)(e,{flexGapSM:r,flexGap:t,flexGapLG:a});return[(e=>{let{componentCls:r}=e;return{[r]:{display:"flex",margin:0,padding:0,"&-vertical":{flexDirection:"column"},"&-rtl":{direction:"rtl"},"&:empty":{display:"none"}}}})(o),(e=>{let{componentCls:r}=e;return{[r]:{"&-gap-small":{gap:e.flexGapSM},"&-gap-middle":{gap:e.flexGap},"&-gap-large":{gap:e.flexGapLG}}}})(o),(e=>{let{componentCls:r}=e,t={};return i.forEach(e=>{t[`${r}-wrap-${e}`]={flexWrap:e}}),t})(o),(e=>{let{componentCls:r}=e,t={};return c.forEach(e=>{t[`${r}-align-${e}`]={alignItems:e}}),t})(o),(e=>{let{componentCls:r}=e,t={};return d.forEach(e=>{t[`${r}-justify-${e}`]={justifyContent:e}}),t})(o)]},()=>({}),{resetStyle:!1});var f=function(e,r){var t={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>r.indexOf(a)&&(t[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,a=Object.getOwnPropertySymbols(e);or.indexOf(a[o])&&Object.prototype.propertyIsEnumerable.call(e,a[o])&&(t[a[o]]=e[a[o]]);return t};let g=r.default.forwardRef((e,s)=>{let{prefixCls:n,rootClassName:i,className:d,style:c,flex:g,gap:p,vertical:b=!1,component:h="div",children:y}=e,v=f(e,["prefixCls","rootClassName","className","style","flex","gap","vertical","component","children"]),{flex:x,direction:C,getPrefixCls:w}=r.default.useContext(l.ConfigContext),k=w("flex",n),[N,T,O]=u(k),E=null!=b?b:null==x?void 0:x.vertical,M=(0,t.default)(d,i,null==x?void 0:x.className,k,T,O,m(k,e),{[`${k}-rtl`]:"rtl"===C,[`${k}-gap-${p}`]:(0,o.isPresetSize)(p),[`${k}-vertical`]:E}),j=Object.assign(Object.assign({},null==x?void 0:x.style),c);return g&&(j.flex=g),p&&!(0,o.isPresetSize)(p)&&(j.gap=p),N(r.default.createElement(h,Object.assign({ref:s,className:M,style:j},(0,a.default)(v,["justify","wrap","align"])),y))});e.s(["Flex",0,g],525720)},743151,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CopyToClipboard=void 0;var a=s(e.r(844343)),o=s(e.r(271645)),l=["text","onCopy","options","children"];function s(e){return e&&e.__esModule?e:{default:e}}function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);r&&(a=a.filter(function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable})),t.push.apply(t,a)}return t}function d(e){for(var r=1;r{"use strict";var a=e.r(743151).CopyToClipboard;a.CopyToClipboard=a,r.exports=a},269200,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=t.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,r.__rest)(e,["children","className"]);return t.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",n)},t.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},i),s))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=t.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",n)},i),s))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=t.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",n)},i),s))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=t.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",n)},i),s))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=t.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",n)},i),s))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=t.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,r.__rest)(e,["children","className"]);return t.default.createElement(t.default.Fragment,null,t.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),n)},i),s))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},536916,e=>{"use strict";var r=e.i(374276);e.s(["Checkbox",()=>r.default])},350967,46757,e=>{"use strict";var r=e.i(290571),t=e.i(444755),a=e.i(673706),o=e.i(271645);let l={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},s={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},n={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,l,"gridColsLg",0,i,"gridColsMd",0,n,"gridColsSm",0,s],46757);let d=(0,a.makeClassName)("Grid"),c=(e,r)=>e&&Object.keys(r).includes(String(e))?r[e]:"",m=o.default.forwardRef((e,a)=>{let{numItems:m=1,numItemsSm:u,numItemsMd:f,numItemsLg:g,children:p,className:b}=e,h=(0,r.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),y=c(m,l),v=c(u,s),x=c(f,n),C=c(g,i),w=(0,t.tremorTwMerge)(y,v,x,C);return o.default.createElement("div",Object.assign({ref:a,className:(0,t.tremorTwMerge)(d("root"),"grid",w,b)},h),p)});m.displayName="Grid",e.s(["Grid",0,m],350967)},981339,e=>{"use strict";var r=e.i(185793);e.s(["Skeleton",()=>r.default])},596239,e=>{"use strict";e.i(247167);var r=e.i(931067),t=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M574 665.4a8.03 8.03 0 00-11.3 0L446.5 781.6c-53.8 53.8-144.6 59.5-204 0-59.5-59.5-53.8-150.2 0-204l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3l-39.8-39.8a8.03 8.03 0 00-11.3 0L191.4 526.5c-84.6 84.6-84.6 221.5 0 306s221.5 84.6 306 0l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3L574 665.4zm258.6-474c-84.6-84.6-221.5-84.6-306 0L410.3 307.6a8.03 8.03 0 000 11.3l39.7 39.7c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c53.8-53.8 144.6-59.5 204 0 59.5 59.5 53.8 150.2 0 204L665.3 562.6a8.03 8.03 0 000 11.3l39.8 39.8c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c84.5-84.6 84.5-221.5 0-306.1zM610.1 372.3a8.03 8.03 0 00-11.3 0L372.3 598.7a8.03 8.03 0 000 11.3l39.6 39.6c3.1 3.1 8.2 3.1 11.3 0l226.4-226.4c3.1-3.1 3.1-8.2 0-11.3l-39.5-39.6z"}}]},name:"link",theme:"outlined"};var o=e.i(9583),l=t.forwardRef(function(e,l){return t.createElement(o.default,(0,r.default)({},e,{ref:l,icon:a}))});e.s(["LinkOutlined",0,l],596239)},751904,e=>{"use strict";var r=e.i(401361);e.s(["EditOutlined",()=>r.default])},727612,e=>{"use strict";let r=(0,e.i(475254).default)("trash-2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);e.s(["Trash2",0,r],727612)},823429,e=>{"use strict";let r=(0,e.i(475254).default)("square-pen",[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]]);e.s(["default",0,r])},465261,e=>{"use strict";let r=(0,e.i(475254).default)("key-round",[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]]);e.s(["KeyRound",0,r],465261)},688511,e=>{"use strict";var r=e.i(823429);e.s(["Edit",()=>r.default])},700514,e=>{"use strict";var r=e.i(271645);e.s(["defaultPageSize",0,25,"useBaseUrl",0,()=>{let[e,t]=(0,r.useState)("http://localhost:4000");return(0,r.useEffect)(()=>{{let{protocol:e,host:r}=window.location;t(`${e}//${r}`)}},[]),e}])},98919,e=>{"use strict";let r=(0,e.i(475254).default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);e.s(["Shield",0,r],98919)},114600,e=>{"use strict";var r=e.i(290571),t=e.i(444755),a=e.i(673706),o=e.i(271645);let l=(0,a.makeClassName)("Divider"),s=o.default.forwardRef((e,a)=>{let{className:s,children:n}=e,i=(0,r.__rest)(e,["className","children"]);return o.default.createElement("div",Object.assign({ref:a,className:(0,t.tremorTwMerge)(l("root"),"w-full mx-auto my-6 flex justify-between gap-3 items-center text-tremor-default","text-tremor-content","dark:text-dark-tremor-content",s)},i),n?o.default.createElement(o.default.Fragment,null,o.default.createElement("div",{className:(0,t.tremorTwMerge)("w-full h-[1px] bg-tremor-border dark:bg-dark-tremor-border")}),o.default.createElement("div",{className:(0,t.tremorTwMerge)("text-inherit whitespace-nowrap")},n),o.default.createElement("div",{className:(0,t.tremorTwMerge)("w-full h-[1px] bg-tremor-border dark:bg-dark-tremor-border")})):o.default.createElement("div",{className:(0,t.tremorTwMerge)("w-full h-[1px] bg-tremor-border dark:bg-dark-tremor-border")}))});s.displayName="Divider",e.s(["Divider",0,s],114600)},366283,e=>{"use strict";var r=e.i(290571),t=e.i(271645),a=e.i(95779),o=e.i(444755),l=e.i(673706);let s=(0,l.makeClassName)("Callout"),n=t.default.forwardRef((e,n)=>{let{title:i,icon:d,color:c,className:m,children:u}=e,f=(0,r.__rest)(e,["title","icon","color","className","children"]);return t.default.createElement("div",Object.assign({ref:n,className:(0,o.tremorTwMerge)(s("root"),"flex flex-col overflow-hidden rounded-tremor-default text-tremor-default border-l-4 py-3 pr-3 pl-4",c?(0,o.tremorTwMerge)((0,l.getColorClassNames)(c,a.colorPalette.background).bgColor,(0,l.getColorClassNames)(c,a.colorPalette.darkBorder).borderColor,(0,l.getColorClassNames)(c,a.colorPalette.darkText).textColor,"dark:bg-opacity-10 bg-opacity-10"):(0,o.tremorTwMerge)("bg-tremor-brand-faint border-tremor-brand-emphasis text-tremor-brand-emphasis","dark:bg-dark-tremor-brand-muted/70 dark:border-dark-tremor-brand-emphasis dark:text-dark-tremor-brand-emphasis"),m)},f),t.default.createElement("div",{className:(0,o.tremorTwMerge)(s("header"),"flex items-start")},d?t.default.createElement(d,{className:(0,o.tremorTwMerge)(s("icon"),"flex-none h-5 w-5 mr-1.5")}):null,t.default.createElement("h4",{className:(0,o.tremorTwMerge)(s("title"),"font-semibold")},i)),t.default.createElement("p",{className:(0,o.tremorTwMerge)(s("body"),"overflow-y-auto",u?"mt-2":"")},u))});n.displayName="Callout",e.s(["Callout",0,n],366283)},475647,e=>{"use strict";e.i(247167);var r=e.i(931067),t=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M696 480H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"plus-circle",theme:"outlined"};var o=e.i(9583),l=t.forwardRef(function(e,l){return t.createElement(o.default,(0,r.default)({},e,{ref:l,icon:a}))});e.s(["PlusCircleOutlined",0,l],475647)},153472,e=>{"use strict";var r,t,a=e.i(266027),o=e.i(954616),l=e.i(912598),s=e.i(243652),n=e.i(135214),i=e.i(602869),d=e.i(431703),c=((r={}).GENERAL_SETTINGS="general_settings",r),m=((t={}).MAXIMUM_SPEND_LOGS_RETENTION_PERIOD="maximum_spend_logs_retention_period",t);let u=async(e,r)=>{try{let t=i.proxyBaseUrl?`${i.proxyBaseUrl}/config/list?config_type=${r}`:`/config/list?config_type=${r}`,a=await fetch(t,{method:"GET",headers:{[(0,i.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!a.ok){let e=await a.json(),r=(0,d.deriveErrorMessage)(e);throw(0,i.handleError)(r),Error(r)}return await a.json()}catch(e){throw console.error(`Failed to get proxy config for ${r}:`,e),e}},f=(0,s.createQueryKeys)("proxyConfig"),g=async(e,r)=>{try{let t=i.proxyBaseUrl?`${i.proxyBaseUrl}/config/field/delete`:"/config/field/delete",a=await fetch(t,{method:"POST",headers:{[(0,i.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!a.ok){let e=await a.json(),r=(0,d.deriveErrorMessage)(e);throw(0,i.handleError)(r),Error(r)}return await a.json()}catch(e){throw console.error(`Failed to delete proxy config field ${r.field_name}:`,e),e}};e.s(["ConfigType",()=>c,"GeneralSettingsFieldName",()=>m,"proxyConfigKeys",0,f,"useDeleteProxyConfigField",0,()=>{let{accessToken:e}=(0,n.default)(),r=(0,l.useQueryClient)();return(0,o.useMutation)({mutationFn:async r=>{if(!e)throw Error("Access token is required");return await g(e,r)},onSuccess:()=>{r.invalidateQueries({queryKey:f.all})}})},"useProxyConfig",0,e=>{let{accessToken:r}=(0,n.default)();return(0,a.useQuery)({queryKey:f.list({filters:{configType:e}}),queryFn:async()=>await u(r,e),enabled:!!r})}])},286536,77705,e=>{"use strict";var r=e.i(475254);let t=(0,r.default)("eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Eye",0,t],286536);let a=(0,r.default)("eye-off",[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",key:"ct8e1f"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242",key:"151rxh"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143",key:"13bj9a"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]]);e.s(["EyeOff",0,a],77705)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/016u~n51r0h1k.js b/litellm/proxy/_experimental/out/_next/static/chunks/016u~n51r0h1k.js new file mode 100644 index 00000000000..bd41af1a6d9 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/016u~n51r0h1k.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,184163,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["default",0,i],184163)},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),n=e.i(201072),o=e.i(121229),i=e.i(726289),a=e.i(864517),l=e.i(343794),s=e.i(529681),c=e.i(242064),u=e.i(931067),d=e.i(209428),p=e.i(703923),f={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},g=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),n=!1;e.current.forEach(function(e){if(e){n=!0;var o=e.style;o.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(o.transitionDuration="0s, 0s")}}),n&&(r.current=Date.now())}),e.current},m=e.i(410160),h=e.i(392221),b=e.i(654310),v=0,y=(0,b.default)();let x=function(e){var r=t.useState(),n=(0,h.default)(r,2),o=n[0],i=n[1];return t.useEffect(function(){var e;i("rc_progress_".concat((y?(e=v,v+=1):e="TEST_OR_SSR",e)))},[]),e||o};var k=function(e){var r=e.bg,n=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},n)};function $(e,t){return Object.keys(e).map(function(r){var n=parseFloat(r),o="".concat(Math.floor(n*t),"%");return"".concat(e[r]," ").concat(o)})}var C=t.forwardRef(function(e,r){var n=e.prefixCls,o=e.color,i=e.gradientId,a=e.radius,l=e.style,s=e.ptg,c=e.strokeLinecap,u=e.strokeWidth,d=e.size,p=e.gapDegree,f=o&&"object"===(0,m.default)(o),g=d/2,h=t.createElement("circle",{className:"".concat(n,"-circle-path"),r:a,cx:g,cy:g,stroke:f?"#FFF":void 0,strokeLinecap:c,strokeWidth:u,opacity:+(0!==s),style:l,ref:r});if(!f)return h;var b="".concat(i,"-conic"),v=$(o,(360-p)/360),y=$(o,1),x="conic-gradient(from ".concat(p?"".concat(180+p/2,"deg"):"0deg",", ").concat(v.join(", "),")"),C="linear-gradient(to ".concat(p?"bottom":"top",", ").concat(y.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:d,height:d,mask:"url(#".concat(b,")")},t.createElement(k,{bg:C},t.createElement(k,{bg:x}))))}),w=function(e,t,r,n,o,i,a,l,s,c){var u=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,d=(100-n)/100*t;return"round"===s&&100!==n&&(d+=c/2)>=t&&(d=t-.01),{stroke:"string"==typeof l?l:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:d+u,transform:"rotate(".concat(o+r/100*360*((360-i)/360)+(0===i?0:({bottom:0,top:180,left:90,right:-90})[a]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let O=function(e){var r,n,o,i,a=(0,d.default)((0,d.default)({},f),e),s=a.id,c=a.prefixCls,h=a.steps,b=a.strokeWidth,v=a.trailWidth,y=a.gapDegree,k=void 0===y?0:y,$=a.gapPosition,O=a.trailColor,j=a.strokeLinecap,_=a.style,N=a.className,I=a.strokeColor,D=a.percent,M=(0,p.default)(a,S),P=x(s),A="".concat(P,"-gradient"),T=50-b/2,z=2*Math.PI*T,R=k>0?90+k/2:-90,W=(360-k)/360*z,L="object"===(0,m.default)(h)?h:{count:h,gap:2},F=L.count,H=L.gap,B=E(D),X=E(I),V=X.find(function(e){return e&&"object"===(0,m.default)(e)}),U=V&&"object"===(0,m.default)(V)?"butt":j,K=w(z,W,0,100,R,k,$,O,U,b),q=g();return t.createElement("svg",(0,u.default)({className:(0,l.default)("".concat(c,"-circle"),N),viewBox:"0 0 ".concat(100," ").concat(100),style:_,id:s,role:"presentation"},M),!F&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:T,cx:50,cy:50,stroke:O,strokeLinecap:U,strokeWidth:v||b,style:K}),F?(r=Math.round(F*(B[0]/100)),n=100/F,o=0,Array(F).fill(null).map(function(e,i){var a=i<=r-1?X[0]:O,l=a&&"object"===(0,m.default)(a)?"url(#".concat(A,")"):void 0,s=w(z,W,o,n,R,k,$,a,"butt",b,H);return o+=(W-s.strokeDashoffset+H)*100/W,t.createElement("circle",{key:i,className:"".concat(c,"-circle-path"),r:T,cx:50,cy:50,stroke:l,strokeWidth:b,opacity:1,style:s,ref:function(e){q[i]=e}})})):(i=0,B.map(function(e,r){var n=X[r]||X[X.length-1],o=w(z,W,i,e,R,k,$,n,U,b);return i+=e,t.createElement(C,{key:r,color:n,ptg:e,radius:T,prefixCls:c,gradientId:A,style:o,strokeLinecap:U,strokeWidth:b,gapDegree:k,ref:function(e){q[r]=e},size:100})}).reverse()))};var j=e.i(491816);e.i(765846);var _=e.i(896091);function N(e){return!e||e<0?0:e>100?100:e}function I({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let D=(e,t,r)=>{var n,o,i,a;let l=-1,s=-1;if("step"===t){let t=r.steps,n=r.strokeWidth;"string"==typeof e||void 0===e?(l="small"===e?2:14,s=null!=n?n:8):"number"==typeof e?[l,s]=[e,e]:[l=14,s=8]=Array.isArray(e)?e:[e.width,e.height],l*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?s=t||("small"===e?6:8):"number"==typeof e?[l,s]=[e,e]:[l=-1,s=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[l,s]="small"===e?[60,60]:[120,120]:"number"==typeof e?[l,s]=[e,e]:Array.isArray(e)&&(l=null!=(o=null!=(n=e[0])?n:e[1])?o:120,s=null!=(a=null!=(i=e[0])?i:e[1])?a:120));return[l,s]},M=e=>{let{prefixCls:r,trailColor:n=null,strokeLinecap:o="round",gapPosition:i,gapDegree:a,width:s=120,type:c,children:u,success:d,size:p=s,steps:f}=e,[g,m]=D(p,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/g*100,6));let b=t.useMemo(()=>a||0===a?a:"dashboard"===c?75:void 0,[a,c]),v=(({percent:e,success:t,successPercent:r})=>{let n=N(I({success:t,successPercent:r}));return[n,N(N(e)-n)]})(e),y="[object Object]"===Object.prototype.toString.call(e.strokeColor),x=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||_.presetPrimaryColors.green,t||null]})({success:d,strokeColor:e.strokeColor}),k=(0,l.default)(`${r}-inner`,{[`${r}-circle-gradient`]:y}),$=t.createElement(O,{steps:f,percent:f?v[1]:v,strokeWidth:h,trailWidth:h,strokeColor:f?x[1]:x,strokeLinecap:o,trailColor:n,prefixCls:r,gapDegree:b,gapPosition:i||"dashboard"===c&&"bottom"||void 0}),C=g<=20,w=t.createElement("div",{className:k,style:{width:g,height:m,fontSize:.15*g+6}},$,!C&&u);return C?t.createElement(j.default,{title:u},w):w};e.i(296059);var P=e.i(694758),A=e.i(915654),T=e.i(183293),z=e.i(246422),R=e.i(838378);let W="--progress-line-stroke-color",L="--progress-percent",F=e=>{let t=e?"100%":"-100%";return new P.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},H=(0,z.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,R.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,T.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${W})`]},height:"100%",width:`calc(1 / var(${L}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,A.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:F(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:F(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var B=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let X=e=>{let{prefixCls:r,direction:n,percent:o,size:i,strokeWidth:a,strokeColor:s,strokeLinecap:c="round",children:u,trailColor:d=null,percentPosition:p,success:f}=e,{align:g,type:m}=p,h=s&&"string"!=typeof s?((e,t)=>{let{from:r=_.presetPrimaryColors.blue,to:n=_.presetPrimaryColors.blue,direction:o="rtl"===t?"to left":"to right"}=e,i=B(e,["from","to","direction"]);if(0!==Object.keys(i).length){let e,t=(e=[],Object.keys(i).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:i[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${o}, ${t})`;return{background:r,[W]:r}}let a=`linear-gradient(${o}, ${r}, ${n})`;return{background:a,[W]:a}})(s,n):{[W]:s,background:s},b="square"===c||"butt"===c?0:void 0,[v,y]=D(null!=i?i:[-1,a||("small"===i?6:8)],"line",{strokeWidth:a}),x=Object.assign(Object.assign({width:`${N(o)}%`,height:y,borderRadius:b},h),{[L]:N(o)/100}),k=I(e),$={width:`${N(k)}%`,height:y,borderRadius:b,backgroundColor:null==f?void 0:f.strokeColor},C=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:d||void 0,borderRadius:b}},t.createElement("div",{className:(0,l.default)(`${r}-bg`,`${r}-bg-${m}`),style:x},"inner"===m&&u),void 0!==k&&t.createElement("div",{className:`${r}-success-bg`,style:$})),w="outer"===m&&"start"===g,S="outer"===m&&"end"===g;return"outer"===m&&"center"===g?t.createElement("div",{className:`${r}-layout-bottom`},C,u):t.createElement("div",{className:`${r}-outer`,style:{width:v<0?"100%":v}},w&&u,C,S&&u)},V=e=>{let{size:r,steps:n,rounding:o=Math.round,percent:i=0,strokeWidth:a=8,strokeColor:s,trailColor:c=null,prefixCls:u,children:d}=e,p=o(i/100*n),[f,g]=D(null!=r?r:["small"===r?2:14,a],"step",{steps:n,strokeWidth:a}),m=f/n,h=Array.from({length:n});for(let e=0;et.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let K=["normal","exception","active","success"],q=t.forwardRef((e,u)=>{let d,{prefixCls:p,className:f,rootClassName:g,steps:m,strokeColor:h,percent:b=0,size:v="default",showInfo:y=!0,type:x="line",status:k,format:$,style:C,percentPosition:w={}}=e,S=U(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:O="outer"}=w,j=Array.isArray(h)?h[0]:h,_="string"==typeof h||Array.isArray(h)?h:void 0,P=t.useMemo(()=>{if(j){let e="string"==typeof j?j:Object.values(j)[0];return new r.FastColor(e).isLight()}return!1},[h]),A=t.useMemo(()=>{var t,r;let n=I(e);return Number.parseInt(void 0!==n?null==(t=null!=n?n:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),T=t.useMemo(()=>!K.includes(k)&&A>=100?"success":k||"normal",[k,A]),{getPrefixCls:z,direction:R,progress:W}=t.useContext(c.ConfigContext),L=z("progress",p),[F,B,q]=H(L),Q="line"===x,Y=Q&&!m,G=t.useMemo(()=>{let r;if(!y)return null;let s=I(e),c=$||(e=>`${e}%`),u=Q&&P&&"inner"===O;return"inner"===O||$||"exception"!==T&&"success"!==T?r=c(N(b),N(s)):"exception"===T?r=Q?t.createElement(i.default,null):t.createElement(a.default,null):"success"===T&&(r=Q?t.createElement(n.default,null):t.createElement(o.default,null)),t.createElement("span",{className:(0,l.default)(`${L}-text`,{[`${L}-text-bright`]:u,[`${L}-text-${E}`]:Y,[`${L}-text-${O}`]:Y}),title:"string"==typeof r?r:void 0},r)},[y,b,A,T,x,L,$]);"line"===x?d=m?t.createElement(V,Object.assign({},e,{strokeColor:_,prefixCls:L,steps:"object"==typeof m?m.count:m}),G):t.createElement(X,Object.assign({},e,{strokeColor:j,prefixCls:L,direction:R,percentPosition:{align:E,type:O}}),G):("circle"===x||"dashboard"===x)&&(d=t.createElement(M,Object.assign({},e,{strokeColor:j,prefixCls:L,progressStatus:T}),G));let J=(0,l.default)(L,`${L}-status-${T}`,{[`${L}-${"dashboard"===x&&"circle"||x}`]:"line"!==x,[`${L}-inline-circle`]:"circle"===x&&D(v,"circle")[0]<=20,[`${L}-line`]:Y,[`${L}-line-align-${E}`]:Y,[`${L}-line-position-${O}`]:Y,[`${L}-steps`]:m,[`${L}-show-info`]:y,[`${L}-${v}`]:"string"==typeof v,[`${L}-rtl`]:"rtl"===R},null==W?void 0:W.className,f,g,B,q);return F(t.createElement("div",Object.assign({ref:u,style:Object.assign(Object.assign({},null==W?void 0:W.style),C),className:J,role:"progressbar","aria-valuenow":A,"aria-valuemin":0,"aria-valuemax":100},(0,s.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),d))});e.s(["default",0,q],309821)},993914,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"}}]},name:"file-text",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["FileTextOutlined",0,i],993914)},663435,152473,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(199133),o=e.i(898586),i=e.i(56456);let a={enabled:!0,leading:!1,trailing:!0,wait:0,onExecute:()=>{}};class l{constructor(e,t){this.fn=e,this._canLeadingExecute=!0,this._isPending=!1,this._executionCount=0,this._options={...a,...t}}setOptions(e){return this._options={...this._options,...e},this._options.enabled||(this._isPending=!1),this._options}getOptions(){return this._options}maybeExecute(...e){this._options.leading&&this._canLeadingExecute&&(this.executeFunction(...e),this._canLeadingExecute=!1),(this._options.leading||this._options.trailing)&&(this._isPending=!0),this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=setTimeout(()=>{this._canLeadingExecute=!0,this._isPending=!1,this._options.trailing&&this.executeFunction(...e)},this._options.wait)}executeFunction(...e){this._options.enabled&&(this.fn(...e),this._executionCount++,this._options.onExecute(this))}cancel(){this._timeoutId&&(clearTimeout(this._timeoutId),this._canLeadingExecute=!0,this._isPending=!1)}getExecutionCount(){return this._executionCount}getIsPending(){return this._options.enabled&&this._isPending}}function s(e,t){let[n,o]=(0,r.useState)(e),i=function(e,t){let[n]=(0,r.useState)(()=>{var r;return Object.getOwnPropertyNames(Object.getPrototypeOf(r=new l(e,t))).filter(e=>"function"==typeof r[e]).reduce((e,t)=>{let n=r[t];return"function"==typeof n&&(e[t]=n.bind(r)),e},{})});return n.setOptions(t),n}(o,t);return[n,i.maybeExecute,i]}e.s(["useDebouncedState",0,s],152473);var c=e.i(785242);let{Text:u}=o.Typography;e.s(["default",0,({value:e,onChange:o,onTeamSelect:a,disabled:l,organizationId:d,pageSize:p=20})=>{let[f,g]=(0,r.useState)(""),[m,h]=s("",{wait:300}),{data:b,fetchNextPage:v,hasNextPage:y,isFetchingNextPage:x,isLoading:k}=(0,c.useInfiniteTeams)(p,m||void 0,d),$=(0,r.useMemo)(()=>{if(!b?.pages)return[];let e=new Set,t=[];for(let r of b.pages)for(let n of r.teams)e.has(n.team_id)||(e.add(n.team_id),t.push(n));return t},[b]);return(0,t.jsx)(n.Select,{showSearch:!0,placeholder:"Search or select a team",value:e||void 0,onChange:e=>{o?.(e??""),a&&a(e?$.find(t=>t.team_id===e)??null:null)},disabled:l,allowClear:!0,filterOption:!1,onSearch:e=>{g(e),h(e)},searchValue:f,onPopupScroll:e=>{let t=e.currentTarget;(t.scrollTop+t.clientHeight)/t.scrollHeight>=.8&&y&&!x&&v()},loading:k,notFoundContent:k?(0,t.jsx)(i.LoadingOutlined,{spin:!0}):"No teams found","data-testid":"team-dropdown",popupRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,x&&(0,t.jsx)("div",{style:{textAlign:"center",padding:8},children:(0,t.jsx)(i.LoadingOutlined,{spin:!0})})]}),children:$.map(e=>(0,t.jsxs)(n.Select.Option,{value:e.team_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.team_alias})," ",(0,t.jsxs)(u,{type:"secondary",children:["(",e.team_id,")"]})]},e.team_id))})}],663435)},519756,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["UploadOutlined",0,i],519756)},435451,e=>{"use strict";var t=e.i(843476),r=e.i(290571),n=e.i(271645);let o=e=>{var t=(0,r.__rest)(e,[]);return n.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),n.default.createElement("path",{d:"M12 4v16m8-8H4"}))},i=e=>{var t=(0,r.__rest)(e,[]);return n.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),n.default.createElement("path",{d:"M20 12H4"}))};var a=e.i(444755),l=e.i(673706),s=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",u="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",d=n.default.forwardRef((e,t)=>{let{onSubmit:d,enableStepper:p=!0,disabled:f,onValueChange:g,onChange:m}=e,h=(0,r.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),b=(0,n.useRef)(null),[v,y]=n.default.useState(!1),x=n.default.useCallback(()=>{y(!0)},[]),k=n.default.useCallback(()=>{y(!1)},[]),[$,C]=n.default.useState(!1),w=n.default.useCallback(()=>{C(!0)},[]),S=n.default.useCallback(()=>{C(!1)},[]);return n.default.createElement(s.default,Object.assign({type:"number",ref:(0,l.mergeRefs)([b,t]),disabled:f,makeInputClassName:(0,l.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=b.current)?void 0:t.value;null==d||d(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&x(),"ArrowUp"===e.key&&w()},onKeyUp:e=>{"ArrowDown"===e.key&&k(),"ArrowUp"===e.key&&S()},onChange:e=>{f||(null==g||g(parseFloat(e.target.value)),null==m||m(e))},stepper:p?n.default.createElement("div",{className:(0,a.tremorTwMerge)("flex justify-center align-middle")},n.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;f||(null==(e=b.current)||e.stepDown(),null==(t=b.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,a.tremorTwMerge)(!f&&u,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},n.default.createElement(i,{"data-testid":"step-down",className:(v?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),n.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;f||(null==(e=b.current)||e.stepUp(),null==(t=b.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,a.tremorTwMerge)(!f&&u,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},n.default.createElement(o,{"data-testid":"step-up",className:($?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},h))});d.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:r={width:"100%"},placeholder:n="Enter a numerical value",min:o,max:i,onChange:a,...l})=>(0,t.jsx)(d,{onWheel:e=>e.currentTarget.blur(),step:e,style:r,placeholder:n,min:o,max:i,onChange:a,...l})],435451)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.1o3m2oaq6je.js b/litellm/proxy/_experimental/out/_next/static/chunks/01dk-b-_masm~.js similarity index 58% rename from litellm/proxy/_experimental/out/_next/static/chunks/0.1o3m2oaq6je.js rename to litellm/proxy/_experimental/out/_next/static/chunks/01dk-b-_masm~.js index 2eeed6a6c76..03fe5143c6c 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0.1o3m2oaq6je.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01dk-b-_masm~.js @@ -1,4 +1,4 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,509345,e=>{"use strict";var t,a,l=e.i(843476),r=e.i(271645),i=e.i(464571),s=e.i(326373),n=e.i(653496),o=e.i(755151),d=e.i(646563),c=e.i(245094),m=e.i(602869),u=e.i(808613),p=e.i(311451),g=e.i(212931),x=e.i(199133),h=e.i(262218),f=e.i(898586),y=e.i(727749),j=e.i(770914),_=e.i(515831),b=e.i(175712),v=e.i(519756);let{Text:w}=f.Typography,{Option:N}=x.Select,C=({visible:e,prebuiltPatterns:t,categories:a,selectedPatternName:r,patternAction:s,onPatternNameChange:n,onActionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add prebuilt pattern",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(w,{strong:!0,children:"Pattern type"}),(0,l.jsx)(x.Select,{placeholder:"Choose pattern type",value:r,onChange:n,style:{width:"100%",marginTop:8},showSearch:!0,filterOption:(e,a)=>{let l=t.find(e=>e.name===a?.value);return!!l&&(l.display_name.toLowerCase().includes(e.toLowerCase())||l.name.toLowerCase().includes(e.toLowerCase()))},children:a.map(e=>{let a=t.filter(t=>t.category===e);return 0===a.length?null:(0,l.jsx)(x.Select.OptGroup,{label:e,children:a.map(e=>(0,l.jsx)(N,{value:e.name,children:e.display_name},e.name))},e)})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(w,{strong:!0,children:"Action"}),(0,l.jsx)(w,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this pattern is detected"}),(0,l.jsxs)(x.Select,{value:s,onChange:o,style:{width:"100%"},children:[(0,l.jsx)(N,{value:"BLOCK",children:"Block"}),(0,l.jsx)(N,{value:"MASK",children:"Mask"})]})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]}),{Text:S}=f.Typography,{Option:k}=x.Select,I=({visible:e,patternName:t,patternRegex:a,patternAction:r,onNameChange:s,onRegexChange:n,onActionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add custom regex pattern",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(S,{strong:!0,children:"Pattern name"}),(0,l.jsx)(p.Input,{placeholder:"e.g., internal_id, employee_code",value:t,onChange:e=>s(e.target.value),style:{marginTop:8}})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(S,{strong:!0,children:"Regex pattern"}),(0,l.jsx)(p.Input,{placeholder:"e.g., ID-[0-9]{6}",value:a,onChange:e=>n(e.target.value),style:{marginTop:8}}),(0,l.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Enter a valid regular expression to match sensitive data"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(S,{strong:!0,children:"Action"}),(0,l.jsx)(S,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this pattern is detected"}),(0,l.jsxs)(x.Select,{value:r,onChange:o,style:{width:"100%"},children:[(0,l.jsx)(k,{value:"BLOCK",children:"Block"}),(0,l.jsx)(k,{value:"MASK",children:"Mask"})]})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]}),{Text:A}=f.Typography,{Option:O}=x.Select,T=({visible:e,keyword:t,action:a,description:r,onKeywordChange:s,onActionChange:n,onDescriptionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add blocked keyword",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Keyword"}),(0,l.jsx)(p.Input,{placeholder:"Enter sensitive keyword or phrase",value:t,onChange:e=>s(e.target.value),style:{marginTop:8}})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Action"}),(0,l.jsx)(A,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this keyword is detected"}),(0,l.jsxs)(x.Select,{value:a,onChange:n,style:{width:"100%"},children:[(0,l.jsx)(O,{value:"BLOCK",children:"Block"}),(0,l.jsx)(O,{value:"MASK",children:"Mask"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Description (optional)"}),(0,l.jsx)(p.Input.TextArea,{placeholder:"Explain why this keyword is sensitive",value:r,onChange:e=>o(e.target.value),rows:3,style:{marginTop:8}})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]});var P=e.i(291542),L=e.i(955135);let{Text:B}=f.Typography,{Option:F}=x.Select,$=({patterns:e,onActionChange:t,onRemove:a})=>{let r=[{title:"Type",dataIndex:"type",key:"type",width:100,render:e=>(0,l.jsx)(h.Tag,{color:"prebuilt"===e?"blue":"green",children:"prebuilt"===e?"Prebuilt":"Custom"})},{title:"Pattern name",dataIndex:"name",key:"name",render:(e,t)=>t.display_name||t.name},{title:"Regex pattern",dataIndex:"pattern",key:"pattern",render:e=>e?(0,l.jsxs)(B,{code:!0,style:{fontSize:12},children:[e.substring(0,40),"..."]}):"-"},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>t(a.id,e),style:{width:120},size:"small",children:[(0,l.jsx)(F,{value:"BLOCK",children:"Block"}),(0,l.jsx)(F,{value:"MASK",children:"Mask"})]})},{title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>a(t.id),children:"Delete"})}];return 0===e.length?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No patterns added."}):(0,l.jsx)(P.Table,{dataSource:e,columns:r,rowKey:"id",pagination:!1,size:"small"})},{Text:E}=f.Typography,{Option:M}=x.Select,R=({keywords:e,onActionChange:t,onRemove:a})=>{let r=[{title:"Keyword",dataIndex:"keyword",key:"keyword"},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>t(a.id,"action",e),style:{width:120},size:"small",children:[(0,l.jsx)(M,{value:"BLOCK",children:"Block"}),(0,l.jsx)(M,{value:"MASK",children:"Mask"})]})},{title:"Description",dataIndex:"description",key:"description",render:e=>e||"-"},{title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>a(t.id),children:"Delete"})}];return 0===e.length?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No keywords added."}):(0,l.jsx)(P.Table,{dataSource:e,columns:r,rowKey:"id",pagination:!1,size:"small"})};var G=e.i(362024),z=e.i(993914);let{Title:D,Text:K}=f.Typography,{Option:q}=x.Select,H=({availableCategories:e,selectedCategories:t,onCategoryAdd:a,onCategoryRemove:s,onCategoryUpdate:n,accessToken:o,pendingSelection:c,onPendingSelectionChange:u})=>{let[p,g]=r.default.useState(""),f=void 0!==c?c:p,y=u||g,[j,_]=r.default.useState({}),[v,w]=r.default.useState({}),[N,C]=r.default.useState({}),[S,k]=r.default.useState([]),[I,A]=r.default.useState(""),[O,T]=r.default.useState(!1),B=async e=>{if(o&&!j[e]){C(t=>({...t,[e]:!0}));try{let t=await (0,m.getCategoryYaml)(o,e),a=t.yaml_content;if("json"===t.file_type)try{let e=JSON.parse(a);a=JSON.stringify(e,null,2)}catch(t){console.warn(`Failed to format JSON for ${e}:`,t)}_(t=>({...t,[e]:a})),w(a=>({...a,[e]:t.file_type||"yaml"}))}catch(t){console.error(`Failed to fetch content for category ${e}:`,t)}finally{C(t=>({...t,[e]:!1}))}}};r.default.useEffect(()=>{if(f&&o){let e=j[f];if(e)return void A(e);T(!0),(0,m.getCategoryYaml)(o,f).then(e=>{let t=e.yaml_content;if("json"===e.file_type)try{let e=JSON.parse(t);t=JSON.stringify(e,null,2)}catch(e){console.warn(`Failed to format JSON for ${f}:`,e)}A(t),_(e=>({...e,[f]:t})),w(t=>({...t,[f]:e.file_type||"yaml"}))}).catch(e=>{console.error(`Failed to fetch preview content for category ${f}:`,e),A("")}).finally(()=>{T(!1)})}else A(""),T(!1)},[f,o]);let F=[{title:"Category",dataIndex:"display_name",key:"display_name",render:(t,a)=>{let r=e.find(e=>e.name===a.category);return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{fontWeight:500},children:t}),r?.description&&(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888",marginTop:"4px"},children:r.description})]})}},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,t)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>n(t.id,"action",e),style:{width:"100%"},children:[(0,l.jsx)(q,{value:"BLOCK",children:(0,l.jsx)(h.Tag,{color:"red",children:"BLOCK"})}),(0,l.jsx)(q,{value:"MASK",children:(0,l.jsx)(h.Tag,{color:"orange",children:"MASK"})})]})},{title:"Severity Threshold",dataIndex:"severity_threshold",key:"severity_threshold",width:180,render:(e,t)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>n(t.id,"severity_threshold",e),style:{width:"100%"},children:[(0,l.jsx)(q,{value:"low",children:"Low"}),(0,l.jsx)(q,{value:"medium",children:"Medium"}),(0,l.jsx)(q,{value:"high",children:"High"})]})},{title:"",key:"actions",width:80,render:(e,t)=>(0,l.jsx)(i.Button,{icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>s(t.id),size:"small",children:"Remove"})}],$=e.filter(e=>!t.some(t=>t.category===e.name));return(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:8},children:[(0,l.jsx)(D,{level:5,style:{margin:0},children:"Blocked topics"}),(0,l.jsx)(K,{type:"secondary",style:{fontSize:12,fontWeight:400},children:"Select topics to block using keyword and semantic analysis"})]}),size:"small",children:[(0,l.jsxs)("div",{style:{marginBottom:16,display:"flex",gap:8},children:[(0,l.jsx)(x.Select,{placeholder:"Select a content category",value:f||void 0,onChange:y,style:{flex:1},showSearch:!0,optionLabelProp:"label",filterOption:(e,t)=>(t?.label?.toString().toLowerCase()??"").includes(e.toLowerCase()),children:$.map(e=>(0,l.jsx)(q,{value:e.name,label:e.display_name,children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{fontWeight:500},children:e.display_name}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#666",marginTop:"2px"},children:e.description})]})},e.name))}),(0,l.jsx)(i.Button,{type:"primary",onClick:()=>{if(!f)return;let l=e.find(e=>e.name===f);!l||t.some(e=>e.category===f)||(a({id:`category-${Date.now()}`,category:l.name,display_name:l.display_name,action:l.default_action,severity_threshold:"medium"}),y(""),A(""))},disabled:!f,icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add"})]}),f&&(0,l.jsxs)("div",{style:{marginBottom:16,padding:"12px",background:"#f9f9f9",border:"1px solid #e0e0e0",borderRadius:"4px"},children:[(0,l.jsxs)("div",{style:{marginBottom:8,fontWeight:500,fontSize:"14px"},children:["Preview: ",e.find(e=>e.name===f)?.display_name,v[f]&&(0,l.jsxs)("span",{style:{marginLeft:8,fontSize:"12px",color:"#888",fontWeight:400},children:["(",v[f]?.toUpperCase(),")"]})]}),O?(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Loading content..."}):I?(0,l.jsx)("pre",{style:{background:"#fff",padding:"12px",borderRadius:"4px",overflow:"auto",maxHeight:"300px",maxWidth:"100%",fontSize:"12px",lineHeight:"1.5",margin:0,border:"1px solid #e0e0e0",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:(0,l.jsx)("code",{children:I})}):(0,l.jsx)("div",{style:{padding:"8px",textAlign:"center",color:"#888",fontSize:"12px"},children:"Unable to load category content"})]}),t.length>0?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(P.Table,{dataSource:t,columns:F,pagination:!1,size:"small",rowKey:"id"}),(0,l.jsx)("div",{style:{marginTop:16},children:(0,l.jsx)(G.Collapse,{activeKey:S,onChange:e=>{let t=Array.isArray(e)?e:e?[e]:[],a=new Set(S);t.forEach(e=>{a.has(e)||j[e]||B(e)}),k(t)},ghost:!0,items:t.map(e=>{let t=(v[e.category]||"yaml").toUpperCase();return{key:e.category,label:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,l.jsx)(z.FileTextOutlined,{}),(0,l.jsxs)("span",{children:["View ",t," for ",e.display_name]})]}),children:N[e.category]?(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Loading content..."}):j[e.category]?(0,l.jsx)("pre",{style:{background:"#f5f5f5",padding:"16px",borderRadius:"4px",overflow:"auto",maxHeight:"400px",fontSize:"12px",lineHeight:"1.5",margin:0},children:(0,l.jsx)("code",{children:j[e.category]})}):(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Content will load when expanded"})}})})})]}):(0,l.jsx)("div",{style:{textAlign:"center",padding:"24px",color:"#888",border:"1px dashed #d9d9d9",borderRadius:"4px"},children:"No blocked topics selected. Add topics to detect and block harmful content."})]})};var U=e.i(790848),J=e.i(28651);let{Title:W,Text:V}=f.Typography,{Option:Y}=x.Select,Q={competitor_intent_type:"airline",brand_self:[],locations:[],policy:{competitor_comparison:"refuse",possible_competitor_comparison:"reframe"},threshold_high:.7,threshold_medium:.45,threshold_low:.3},X=({enabled:e,config:t,onChange:a,accessToken:i})=>{let s=t??Q,[n,o]=(0,r.useState)([]),[d,c]=(0,r.useState)(!1);(0,r.useEffect)(()=>{"airline"===s.competitor_intent_type&&i&&0===n.length&&(c(!0),(0,m.getMajorAirlines)(i).then(e=>o(e.airlines??[])).catch(()=>o([])).finally(()=>c(!1)))},[s.competitor_intent_type,i,n.length]);let p=e=>{a(e,e?{...Q}:null)},g=(t,l)=>{a(e,{...s,[t]:l})},h=(t,l)=>{a(e,{...s,policy:{...s.policy,[t]:l}})},f=(t,l)=>{a(e,{...s,[t]:l.filter(Boolean)})};return e?(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(W,{level:5,style:{margin:0},children:"Competitor Intent Filter"}),(0,l.jsx)(U.Switch,{checked:e,onChange:p})]}),size:"small",children:[(0,l.jsx)(V,{type:"secondary",style:{display:"block",marginBottom:16},children:"Block or reframe competitor comparison questions. Airline type uses major airlines (excluding your brand); generic requires manual competitor list."}),(0,l.jsxs)(u.Form,{layout:"vertical",size:"small",children:[(0,l.jsx)(u.Form.Item,{label:"Type",children:(0,l.jsxs)(x.Select,{value:s.competitor_intent_type,onChange:e=>g("competitor_intent_type",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"airline",children:"Airline (auto-load competitors from IATA)"}),(0,l.jsx)(Y,{value:"generic",children:"Generic (specify competitors manually)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Your Brand (brand_self)",required:!0,help:"airline"===s.competitor_intent_type?"Select your airline from the list (excluded from competitors) or type to add a custom term":"Names/codes users use for your brand",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:d?"Loading airlines...":"airline"===s.competitor_intent_type?"Search or select airline, or type to add custom":"Type and press Enter to add",value:s.brand_self,onChange:t=>"airline"===s.competitor_intent_type&&n.length>0?(t=>{let l=t.filter(Boolean),r=[],i=new Set;for(let e of l){let t=n.find(t=>t.match.split("|")[0]?.trim().toLowerCase()===e.toLowerCase());if(t)for(let e of t.match.split("|").map(e=>e.trim().toLowerCase()).filter(Boolean))i.has(e)||(i.add(e),r.push(e));else i.has(e.toLowerCase())||(i.add(e.toLowerCase()),r.push(e))}a(e,{...s,brand_self:r})})(t??[]):f("brand_self",t??[]),tokenSeparators:[","],loading:d,showSearch:!0,filterOption:(e,t)=>(t?.label?.toString().toLowerCase()??"").includes(e.toLowerCase()),optionFilterProp:"label",options:"airline"===s.competitor_intent_type&&n.length>0?n.map(e=>{let t=e.match.split("|")[0]?.trim()??e.id,a=e.match.split("|").map(e=>e.trim().toLowerCase()).filter(Boolean);return{value:t.toLowerCase(),label:`${t}${a.length>1?` (${a.slice(1).join(", ")})`:""}`}}):void 0})}),"airline"===s.competitor_intent_type&&(0,l.jsx)(u.Form.Item,{label:"Locations (optional)",help:"Countries, cities, airports for disambiguation (e.g. qatar, doha)",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type and press Enter to add",value:s.locations??[],onChange:e=>f("locations",e??[]),tokenSeparators:[","]})}),"generic"===s.competitor_intent_type&&(0,l.jsx)(u.Form.Item,{label:"Competitors",required:!0,help:"Competitor names to detect (required for generic type)",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type and press Enter to add",value:s.competitors??[],onChange:e=>f("competitors",e??[]),tokenSeparators:[","]})}),(0,l.jsx)(u.Form.Item,{label:"Policy: Competitor comparison",children:(0,l.jsxs)(x.Select,{value:s.policy?.competitor_comparison??"refuse",onChange:e=>h("competitor_comparison",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"refuse",children:"Refuse (block request)"}),(0,l.jsx)(Y,{value:"reframe",children:"Reframe (suggest alternative)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Policy: Possible competitor comparison",children:(0,l.jsxs)(x.Select,{value:s.policy?.possible_competitor_comparison??"reframe",onChange:e=>h("possible_competitor_comparison",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"refuse",children:"Refuse (block request)"}),(0,l.jsx)(Y,{value:"reframe",children:"Reframe (suggest alternative to backend LLM)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Confidence thresholds",help:(0,l.jsxs)(l.Fragment,{children:["Classify competitor intent by confidence (0–1). Higher confidence → stronger intent.",(0,l.jsxs)("ul",{style:{marginBottom:0,marginTop:4,paddingLeft:20},children:[(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"High (≥)"}),': Treat as full competitor comparison → uses "Competitor comparison" policy']}),(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"Medium (≥)"}),': Treat as possible comparison → uses "Possible competitor comparison" policy']}),(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"Low (≥)"}),": Log only; allow request. Below Low → allow with no action"]})]}),"Raise thresholds to be more permissive; lower them to be stricter."]}),children:(0,l.jsxs)(j.Space,{wrap:!0,children:[(0,l.jsx)(u.Form.Item,{label:"High",style:{marginBottom:0},help:"e.g. 0.7",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_high??.7,onChange:e=>g("threshold_high",e??.7),style:{width:80}})}),(0,l.jsx)(u.Form.Item,{label:"Medium",style:{marginBottom:0},help:"e.g. 0.45",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_medium??.45,onChange:e=>g("threshold_medium",e??.45),style:{width:80}})}),(0,l.jsx)(u.Form.Item,{label:"Low",style:{marginBottom:0},help:"e.g. 0.3",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_low??.3,onChange:e=>g("threshold_low",e??.3),style:{width:80}})})]})})]})]}):(0,l.jsx)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(W,{level:5,style:{margin:0},children:"Competitor Intent Filter"}),(0,l.jsx)(U.Switch,{checked:!1,onChange:p})]}),size:"small",children:(0,l.jsx)(V,{type:"secondary",children:"Block or reframe competitor comparison questions. When enabled, airline type auto-loads competitors from IATA; generic type requires manual competitor list."})})},{Title:Z,Text:ee}=f.Typography,et=({prebuiltPatterns:e,categories:t,selectedPatterns:a,blockedWords:s,onPatternAdd:n,onPatternRemove:o,onPatternActionChange:c,onBlockedWordAdd:u,onBlockedWordRemove:p,onBlockedWordUpdate:g,onFileUpload:x,accessToken:h,showStep:f,contentCategories:w=[],selectedContentCategories:N=[],onContentCategoryAdd:S,onContentCategoryRemove:k,onContentCategoryUpdate:A,pendingCategorySelection:O,onPendingCategorySelectionChange:P,competitorIntentEnabled:L=!1,competitorIntentConfig:B=null,onCompetitorIntentChange:F})=>{let[E,M]=(0,r.useState)(!1),[G,z]=(0,r.useState)(!1),[D,K]=(0,r.useState)(!1),[q,U]=(0,r.useState)(""),[J,W]=(0,r.useState)("BLOCK"),[V,Y]=(0,r.useState)(""),[Q,et]=(0,r.useState)(""),[ea,el]=(0,r.useState)("BLOCK"),[er,ei]=(0,r.useState)(""),[es,en]=(0,r.useState)("BLOCK"),[eo,ed]=(0,r.useState)(""),[ec,em]=(0,r.useState)(!1),eu=async e=>{em(!0);try{let t=await e.text();if(h){let e=await (0,m.validateBlockedWordsFile)(h,t);if(e.valid)x&&x(t),y.default.success(e.message||"File uploaded successfully");else{let t=e.error||e.errors&&e.errors.join(", ")||"Invalid file";y.default.error(`Validation failed: ${t}`)}}}catch(e){y.default.error(`Failed to upload file: ${e}`)}finally{em(!1)}return!1};return(0,l.jsxs)("div",{className:"space-y-6",children:[!f&&(0,l.jsx)("div",{children:(0,l.jsx)(ee,{type:"secondary",children:"Configure patterns, keywords, and content categories to detect and filter sensitive information in requests and responses."})}),(!f||"patterns"===f)&&(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(Z,{level:5,style:{margin:0},children:"Pattern Detection"}),(0,l.jsx)(ee,{type:"secondary",style:{fontSize:14,fontWeight:400},children:"Detect sensitive information using regex patterns (SSN, credit cards, API keys, etc.)"})]}),size:"small",children:[(0,l.jsx)("div",{style:{marginBottom:16},children:(0,l.jsxs)(j.Space,{children:[(0,l.jsx)(i.Button,{type:"primary",onClick:()=>M(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add prebuilt pattern"}),(0,l.jsx)(i.Button,{onClick:()=>K(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add custom regex"})]})}),(0,l.jsx)($,{patterns:a,onActionChange:c,onRemove:o})]}),(!f||"keywords"===f)&&(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(Z,{level:5,style:{margin:0},children:"Blocked Keywords"}),(0,l.jsx)(ee,{type:"secondary",style:{fontSize:14,fontWeight:400},children:"Block or mask specific sensitive terms and phrases"})]}),size:"small",children:[(0,l.jsx)("div",{style:{marginBottom:16},children:(0,l.jsxs)(j.Space,{children:[(0,l.jsx)(i.Button,{type:"primary",onClick:()=>z(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add keyword"}),(0,l.jsx)(_.Upload,{beforeUpload:eu,accept:".yaml,.yml",showUploadList:!1,children:(0,l.jsx)(i.Button,{icon:(0,l.jsx)(v.UploadOutlined,{}),loading:ec,children:"Upload YAML file"})})]})}),(0,l.jsx)(R,{keywords:s,onActionChange:g,onRemove:p})]}),(!f||"competitor_intent"===f||"categories"===f)&&F&&(0,l.jsx)(X,{enabled:L,config:B,onChange:F,accessToken:h}),(!f||"categories"===f)&&w.length>0&&S&&k&&A&&(0,l.jsx)(H,{availableCategories:w,selectedCategories:N,onCategoryAdd:S,onCategoryRemove:k,onCategoryUpdate:A,accessToken:h,pendingSelection:O,onPendingSelectionChange:P}),(0,l.jsx)(C,{visible:E,prebuiltPatterns:e,categories:t,selectedPatternName:q,patternAction:J,onPatternNameChange:U,onActionChange:e=>W(e),onAdd:()=>{if(!q)return void y.default.error("Please select a pattern");let t=e.find(e=>e.name===q);n({id:`pattern-${Date.now()}`,type:"prebuilt",name:q,display_name:t?.display_name,action:J}),M(!1),U(""),W("BLOCK")},onCancel:()=>{M(!1),U(""),W("BLOCK")}}),(0,l.jsx)(I,{visible:D,patternName:V,patternRegex:Q,patternAction:ea,onNameChange:Y,onRegexChange:et,onActionChange:e=>el(e),onAdd:()=>{V&&Q?(n({id:`custom-${Date.now()}`,type:"custom",name:V,pattern:Q,action:ea}),K(!1),Y(""),et(""),el("BLOCK")):y.default.error("Please provide pattern name and regex")},onCancel:()=>{K(!1),Y(""),et(""),el("BLOCK")}}),(0,l.jsx)(T,{visible:G,keyword:er,action:es,description:eo,onKeywordChange:ei,onActionChange:e=>en(e),onDescriptionChange:ed,onAdd:()=>{er?(u({id:`word-${Date.now()}`,keyword:er,action:es,description:eo||void 0}),z(!1),ei(""),ed(""),en("BLOCK")):y.default.error("Please enter a keyword")},onCancel:()=>{z(!1),ei(""),ed(""),en("BLOCK")}})]})};var ea=e.i(555987),el=((t={}).PresidioPII="Presidio PII",t.Bedrock="Bedrock Guardrail",t.Lakera="Lakera",t);let er={},ei=e=>{let t={};return t.PresidioPII="Presidio PII",t.Bedrock="Bedrock Guardrail",t.Lakera="Lakera",t.LlmAsAJudge="LiteLLM LLM as a Judge",Object.entries(e).forEach(([e,a])=>{a&&"object"==typeof a&&"ui_friendly_name"in a&&(t[e.split("_").map((e,t)=>e.charAt(0).toUpperCase()+e.slice(1)).join("")]=a.ui_friendly_name)}),er=t,t},es=()=>Object.keys(er).length>0?er:el,en={PresidioPII:"presidio",Bedrock:"bedrock",Lakera:"lakera_v2",LitellmContentFilter:"litellm_content_filter",ToolPermission:"tool_permission",BlockCodeExecution:"block_code_execution",Promptguard:"promptguard",LlmAsAJudge:"llm_as_a_judge",Xecguard:"xecguard",QostodianNexus:"qostodian_nexus",Repelloai:"repelloai"},eo=e=>{Object.entries(e).forEach(([e,t])=>{t&&"object"==typeof t&&"ui_friendly_name"in t&&(en[e.split("_").map((e,t)=>e.charAt(0).toUpperCase()+e.slice(1)).join("")]=e)})},ed=e=>!!e&&"Presidio PII"===es()[e],ec=e=>!!e&&"LiteLLM Content Filter"===es()[e],em=e=>!!e&&"llm_as_a_judge"===en[e],eu="/ui/assets/logos/",ep={"Zscaler AI Guard":`${eu}zscaler.svg`,"Presidio PII":`${eu}microsoft_azure.svg`,"Bedrock Guardrail":`${eu}bedrock.svg`,Lakera:`${eu}lakeraai.jpeg`,"Azure Content Safety Prompt Shield":`${eu}microsoft_azure.svg`,"Azure Content Safety Text Moderation":`${eu}microsoft_azure.svg`,"Aporia AI":`${eu}aporia.png`,"PANW Prisma AIRS":`${eu}palo_alto_networks.jpeg`,"Cisco AI Defense":`${eu}cisco.png`,"Noma Security":`${eu}noma_security.png`,"Javelin Guardrails":`${eu}javelin.png`,"Pillar Guardrail":`${eu}pillar.jpeg`,"Google Cloud Model Armor":`${eu}google.svg`,"Guardrails AI":`${eu}guardrails_ai.jpeg`,"Lasso Guardrail":`${eu}lasso.png`,"Pangea Guardrail":`${eu}pangea.png`,"AIM Guardrail":`${eu}aim_security.jpeg`,"Cato Networks Guardrail":`${eu}cato_networks.svg`,"OpenAI Moderation":`${eu}openai_small.svg`,EnkryptAI:`${eu}enkrypt_ai.avif`,"Prompt Security":`${eu}prompt_security.png`,PromptGuard:`${eu}promptguard.svg`,XecGuard:`${eu}xecguard.svg`,"LiteLLM Content Filter":`${eu}litellm_logo.jpg`,"LiteLLM LLM as a Judge":`${eu}litellm_logo.jpg`,Akto:`${eu}akto.svg`,"Qostodian Nexus":`${eu}qohash.jpg`,"RepelloAI Argus":`${eu}repelloai.png`},eg=e=>{if(!e)return{logo:"",displayName:"-"};let t=Object.keys(en).find(t=>en[t].toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let a=es()[t];return{logo:(0,ea.resolveLogoSrc)(ep[a])??"",displayName:a||e}};function ex(e){return!0===e?"yes":!1===e?"no":"inherit"}function eh(e){return!0===e?"yes":!1===e?"no":"inherit"}var ef=e.i(435451);let{Title:ey}=f.Typography,ej=({field:e,fieldKey:t,fullFieldKey:a,value:s})=>{let[n,o]=r.default.useState([]),[d,c]=r.default.useState(e.dict_key_options||[]);return r.default.useEffect(()=>{if(s&&"object"==typeof s){let t=Object.keys(s);o(t.map(e=>({key:e,id:`${e}_${Date.now()}_${Math.random()}`}))),c((e.dict_key_options||[]).filter(e=>!t.includes(e)))}},[s,e.dict_key_options]),(0,l.jsxs)("div",{className:"space-y-3",children:[n.map(t=>(0,l.jsxs)("div",{className:"flex items-center space-x-3 p-3 border rounded-lg",children:[(0,l.jsx)("div",{className:"w-24 font-medium text-sm",children:t.key}),(0,l.jsx)("div",{className:"flex-1",children:(0,l.jsx)(u.Form.Item,{name:Array.isArray(a)?[...a,t.key]:[a,t.key],style:{marginBottom:0},initialValue:s&&"object"==typeof s?s[t.key]:void 0,normalize:"number"===e.dict_value_type?e=>{if(null==e||""===e)return;let t=Number(e);return isNaN(t)?e:t}:void 0,children:"number"===e.dict_value_type?(0,l.jsx)(ef.default,{step:1,width:200,placeholder:`Enter ${t.key} value`}):"boolean"===e.dict_value_type?(0,l.jsxs)(x.Select,{placeholder:`Select ${t.key} value`,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):(0,l.jsx)(p.Input,{placeholder:`Enter ${t.key} value`})})}),(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",onClick:()=>{var e,a;return e=t.id,a=t.key,void(o(n.filter(t=>t.id!==e)),c([...d,a].sort()))},children:"Remove"})]},t.id)),d.length>0&&(0,l.jsxs)("div",{className:"flex items-center space-x-3 mt-2",children:[(0,l.jsx)(x.Select,{placeholder:"Select category to configure",style:{width:200},onSelect:e=>e&&void(!e||(o([...n,{key:e,id:`${e}_${Date.now()}`}]),c(d.filter(t=>t!==e)))),value:void 0,children:d.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}),(0,l.jsx)("span",{className:"text-sm text-gray-500",children:"Select a category to add threshold configuration"})]})]})},e_=({optionalParams:e,parentFieldKey:t,values:a})=>e.fields&&0!==Object.keys(e.fields).length?(0,l.jsxs)("div",{className:"guardrail-optional-params",children:[(0,l.jsxs)("div",{className:"mb-8 pb-4 border-b border-gray-100",children:[(0,l.jsx)(ey,{level:3,className:"mb-2 font-semibold text-gray-900",children:"Optional Parameters"}),(0,l.jsx)("p",{className:"text-gray-600 text-sm",children:e.description||"Configure additional settings for this guardrail provider"})]}),(0,l.jsx)("div",{className:"space-y-8",children:Object.entries(e.fields).map(([e,r])=>{let i,s;return i=`${t}.${e}`,s=a?.[e],"dict"===r.type&&r.dict_key_options?(0,l.jsxs)("div",{className:"mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200",children:[(0,l.jsx)("div",{className:"mb-4 font-medium text-gray-900 text-base",children:e}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mb-4",children:r.description}),(0,l.jsx)(ej,{field:r,fieldKey:e,fullFieldKey:[t,e],value:s})]},i):(0,l.jsx)("div",{className:"mb-8 p-6 bg-white rounded-lg border border-gray-200 shadow-xs",children:(0,l.jsx)(u.Form.Item,{name:[t,e],label:(0,l.jsxs)("div",{className:"mb-2",children:[(0,l.jsx)("div",{className:"font-medium text-gray-900 text-base",children:e}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mt-1",children:r.description})]}),rules:r.required?[{required:!0,message:`${e} is required`}]:void 0,className:"mb-0",initialValue:void 0!==s?s:r.default_value,normalize:"number"===r.type?e=>{if(null==e||""===e)return;let t=Number(e);return isNaN(t)?e:t}:void 0,children:"select"===r.type&&r.options?(0,l.jsx)(x.Select,{placeholder:r.description,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"multiselect"===r.type&&r.options?(0,l.jsx)(x.Select,{mode:"multiple",placeholder:r.description,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"bool"===r.type||"boolean"===r.type?(0,l.jsxs)(x.Select,{placeholder:r.description,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):"number"===r.type?(0,l.jsx)(ef.default,{step:1,width:400,placeholder:r.description}):e.includes("password")||e.includes("secret")||e.includes("key")?(0,l.jsx)(p.Input.Password,{placeholder:r.description}):(0,l.jsx)(p.Input,{placeholder:r.description})})},i)})})]}):null;var eb=e.i(482725),ev=e.i(850627);let ew=({selectedProvider:e,accessToken:t,providerParams:a=null,value:i=null})=>{let[s,n]=(0,r.useState)(!1),[o,d]=(0,r.useState)(a),[c,g]=(0,r.useState)(null);if((0,r.useEffect)(()=>{if(a)return void d(a);let e=async()=>{if(t){n(!0),g(null);try{let e=await (0,m.getGuardrailProviderSpecificParams)(t);d(e),ei(e),eo(e)}catch(e){console.error("Error fetching provider params:",e),g("Failed to load provider parameters")}finally{n(!1)}}};a||e()},[t,a]),!e)return null;if(s)return(0,l.jsx)(eb.Spin,{tip:"Loading provider parameters..."});if(c)return(0,l.jsx)("div",{className:"text-red-500",children:c});let h=en[e]?.toLowerCase(),f=o&&o[h];if(!f||0===Object.keys(f).length)return(0,l.jsx)("div",{children:"No configuration fields available for this provider."});let y=new Set(["patterns","blocked_words","blocked_words_file","categories","severity_threshold","pattern_redaction_format","keyword_redaction_tag"]),j=ec(e),_=(e,t="",a)=>Object.entries(e).map(([e,r])=>{let s=t?`${t}.${e}`:e,n=a?a[e]:i?.[e];if("ui_friendly_name"===e||"optional_params"===e&&"nested"===r.type&&r.fields||j&&y.has(e))return null;if("nested"===r.type&&r.fields)return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"mb-2 font-medium",children:e}),(0,l.jsx)("div",{className:"ml-4 border-l-2 border-gray-200 pl-4",children:_(r.fields,s,n)})]},s);let o=void 0!==n?n:r.default_value??("percentage"===r.type?.5:void 0);return(0,l.jsx)(u.Form.Item,{name:s,label:e,tooltip:r.description,rules:r.required?[{required:!0,message:`${e} is required`}]:void 0,initialValue:o,children:"select"===r.type&&r.options?(0,l.jsx)(x.Select,{placeholder:r.description,defaultValue:n||r.default_value,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"multiselect"===r.type&&r.options?(0,l.jsx)(x.Select,{mode:"multiple",placeholder:r.description,defaultValue:n||r.default_value,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"bool"===r.type||"boolean"===r.type?(0,l.jsxs)(x.Select,{placeholder:r.description,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):"percentage"===r.type&&null!=r.min&&null!=r.max?(0,l.jsx)(ev.Slider,{min:r.min,max:r.max,step:r.step??.1,marks:{[r.min]:"0%",[(r.min+r.max)/2]:"50%",[r.max]:"100%"}}):"number"===r.type?(0,l.jsx)(ef.default,{step:1,width:400,placeholder:r.description,defaultValue:void 0!==n?Number(n):void 0}):e.includes("password")||e.includes("secret")||e.includes("key")?(0,l.jsx)(p.Input.Password,{placeholder:r.description,defaultValue:n||""}):(0,l.jsx)(p.Input,{placeholder:r.description,defaultValue:n||""})},s)});return(0,l.jsx)(l.Fragment,{children:_(f)})};var eN=e.i(592968),eC=e.i(750113);let eS=({availableModels:e,form:t})=>(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{style:{background:"#f6ffed",border:"1px solid #b7eb8f",borderRadius:6,padding:"10px 14px",marginBottom:16,fontSize:13,color:"#389e0d"},children:["After each LLM response, the ",(0,l.jsx)("strong",{children:"Judge Model"})," scores it 0–100 against your criteria. If the weighted average falls below the threshold, the response is blocked (or logged)."]}),(0,l.jsx)(u.Form.Item,{name:"judge_model",label:(0,l.jsxs)("span",{children:["Judge Model ",(0,l.jsx)(eN.Tooltip,{title:"The LLM that reads each response and grades it. Pick a capable model — it never sees end-user data beyond what the LLM returned.",children:(0,l.jsx)(eC.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),rules:[{required:!0,message:"Select a judge model"}],children:(0,l.jsx)(x.Select,{showSearch:!0,placeholder:"Select a model",options:e.map(e=>({label:e,value:e}))})}),(0,l.jsx)(u.Form.Item,{name:"overall_threshold",label:(0,l.jsxs)("span",{children:["Minimum Score to Pass ",(0,l.jsx)(eN.Tooltip,{title:"0–100. If the weighted average of criterion scores falls below this, the guardrail triggers. 80 is a good default.",children:(0,l.jsx)(eC.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),initialValue:80,children:(0,l.jsx)(J.InputNumber,{min:0,max:100,addonAfter:"/ 100",style:{width:"100%"}})}),(0,l.jsx)(u.Form.Item,{name:"on_failure",label:(0,l.jsxs)("span",{children:["On Failure ",(0,l.jsx)(eN.Tooltip,{title:"Block: return HTTP 422 when the score is too low. Log: record the result but let the response through.",children:(0,l.jsx)(eC.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),initialValue:"block",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"block",children:"Block (return 422)"}),(0,l.jsx)(x.Select.Option,{value:"log",children:"Log only"})]})}),(0,l.jsx)(u.Form.Item,{label:(0,l.jsxs)("span",{children:["Evaluation Criteria ",(0,l.jsx)(eN.Tooltip,{title:"Each criterion is something the judge checks. Weights must add up to 100%.",children:(0,l.jsx)(eC.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),children:(0,l.jsx)(u.Form.List,{name:"criteria",initialValue:[{name:"",weight:100,description:""}],children:(e,{add:a,remove:r})=>(0,l.jsxs)(l.Fragment,{children:[e.map(({key:e,name:t,...a})=>(0,l.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,padding:"12px 12px 0",marginBottom:8},children:[(0,l.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"flex-end"},children:[(0,l.jsx)(u.Form.Item,{...a,name:[t,"name"],rules:[{required:!0,message:"Enter criterion name"}],style:{flex:2,marginBottom:8},children:(0,l.jsx)(p.Input,{placeholder:"Criterion name (e.g. Policy accuracy)"})}),(0,l.jsx)(u.Form.Item,{...a,name:[t,"weight"],label:(0,l.jsx)(eN.Tooltip,{title:"How much this criterion counts toward the final score. All weights must add up to 100%.",children:(0,l.jsxs)("span",{style:{fontSize:12,color:"#595959"},children:["Weight ",(0,l.jsx)(eC.QuestionCircleOutlined,{style:{color:"#bfbfbf"}})]})}),rules:[{required:!0,message:"Enter weight"}],style:{flex:1,marginBottom:8},children:(0,l.jsx)(J.InputNumber,{min:0,max:100,addonAfter:"%",style:{width:"100%"},placeholder:"e.g. 50"})}),(0,l.jsx)("div",{style:{marginBottom:8},children:(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",onClick:()=>r(t),children:"×"})})]}),(0,l.jsx)(u.Form.Item,{...a,name:[t,"description"],rules:[{required:!0,message:"Describe what to check"}],style:{marginBottom:8},children:(0,l.jsx)(p.Input,{placeholder:"What should the judge check for this criterion?"})})]},e)),(0,l.jsx)(i.Button,{type:"dashed",block:!0,style:{marginTop:4},onClick:()=>a({name:"",weight:0,description:""}),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add Criterion"}),e.length>0&&(0,l.jsx)(u.Form.Item,{shouldUpdate:!0,noStyle:!0,children:()=>{let e=(t.getFieldValue("criteria")||[]).reduce((e,t)=>e+(Number(t?.weight)||0),0),a=100===e;return(0,l.jsxs)("div",{style:{marginTop:6,fontSize:12,color:a?"#52c41a":"#faad14"},children:["Weights total: ",e,"%",a?" ✓":" — must add up to 100%"]})}})]})})})]});var ek=e.i(536916),eI=e.i(149192),eA=e.i(741585),eA=eA,eO=e.i(724154);e.i(247167);var eT=e.i(931067);let eP={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880.1 154H143.9c-24.5 0-39.8 26.7-27.5 48L349 597.4V838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V597.4L907.7 202c12.2-21.3-3.1-48-27.6-48zM603.4 798H420.6V642h182.9v156zm9.6-236.6l-9.5 16.6h-183l-9.5-16.6L212.7 226h598.6L613 561.4z"}}]},name:"filter",theme:"outlined"};var eL=e.i(9583),eB=r.forwardRef(function(e,t){return r.createElement(eL.default,(0,eT.default)({},e,{ref:t,icon:eP}))});let{Text:eF}=f.Typography,{Option:e$}=x.Select,eE=({categories:e,selectedCategories:t,onChange:a})=>(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center mb-2",children:[(0,l.jsx)(eB,{className:"text-gray-500 mr-1"}),(0,l.jsx)(eF,{className:"text-gray-500 font-medium",children:"Filter by category"})]}),(0,l.jsx)(x.Select,{mode:"multiple",placeholder:"Select categories to filter by",style:{width:"100%"},onChange:a,value:t,allowClear:!0,showSearch:!0,optionFilterProp:"children",className:"mb-4",tagRender:e=>(0,l.jsx)(h.Tag,{color:"blue",closable:e.closable,onClose:e.onClose,className:"mr-2 mb-2",children:e.label}),children:e.map(e=>(0,l.jsx)(e$,{value:e.category,children:e.category},e.category))})]}),eM=({onSelectAll:e,onUnselectAll:t,hasSelectedEntities:a})=>(0,l.jsxs)("div",{className:"bg-gray-50 p-5 rounded-lg mb-6 border border-gray-200 shadow-xs",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-4",children:[(0,l.jsxs)("div",{className:"flex items-center",children:[(0,l.jsx)(eF,{strong:!0,className:"text-gray-700 text-base",children:"Quick Actions"}),(0,l.jsx)(eN.Tooltip,{title:"Apply action to all PII types at once",children:(0,l.jsx)("div",{className:"ml-2 text-gray-400 cursor-help text-xs",children:"ⓘ"})})]}),(0,l.jsx)(i.Button,{color:"danger",variant:"outlined",onClick:t,disabled:!a,icon:(0,l.jsx)(eI.CloseOutlined,{}),children:"Unselect All"})]}),(0,l.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,l.jsx)(i.Button,{color:"primary",variant:"outlined",onClick:()=>e("MASK"),className:"h-10",block:!0,icon:(0,l.jsx)(eA.default,{}),children:"Select All & Mask"}),(0,l.jsx)(i.Button,{color:"danger",variant:"outlined",onClick:()=>e("BLOCK"),className:"h-10 hover:bg-red-100",block:!0,icon:(0,l.jsx)(eO.StopOutlined,{}),children:"Select All & Block"})]})]}),eR=({entities:e,selectedEntities:t,selectedActions:a,actions:r,onEntitySelect:i,onActionSelect:s,entityToCategoryMap:n})=>(0,l.jsxs)("div",{className:"border rounded-lg overflow-hidden shadow-xs",children:[(0,l.jsxs)("div",{className:"bg-gray-50 px-5 py-3 border-b flex",children:[(0,l.jsx)(eF,{strong:!0,className:"flex-1 text-gray-700",children:"PII Type"}),(0,l.jsx)(eF,{strong:!0,className:"w-32 text-right text-gray-700",children:"Action"})]}),(0,l.jsx)("div",{className:"max-h-[400px] overflow-y-auto",children:0===e.length?(0,l.jsx)("div",{className:"py-10 text-center text-gray-500",children:"No PII types match your filter criteria"}):e.map(e=>(0,l.jsxs)("div",{className:`px-5 py-3 flex items-center justify-between hover:bg-gray-50 border-b ${t.includes(e)?"bg-blue-50":""}`,children:[(0,l.jsxs)("div",{className:"flex items-center flex-1",children:[(0,l.jsx)(ek.Checkbox,{checked:t.includes(e),onChange:()=>i(e),className:"mr-3"}),(0,l.jsx)(eF,{className:t.includes(e)?"font-medium text-gray-900":"text-gray-700",children:e.replace(/_/g," ")}),n.get(e)&&(0,l.jsx)(h.Tag,{className:"ml-2 text-xs",color:"blue",children:n.get(e)})]}),(0,l.jsx)("div",{className:"w-32",children:(0,l.jsx)(x.Select,{value:t.includes(e)&&a[e]||"MASK",onChange:t=>s(e,t),style:{width:120},disabled:!t.includes(e),className:`${!t.includes(e)?"opacity-50":""}`,dropdownMatchSelectWidth:!1,children:r.map(e=>(0,l.jsx)(e$,{value:e,children:(0,l.jsxs)("div",{className:"flex items-center",children:[(e=>{switch(e){case"MASK":return(0,l.jsx)(eA.default,{style:{marginRight:4}});case"BLOCK":return(0,l.jsx)(eO.StopOutlined,{style:{marginRight:4}});default:return null}})(e),e]})},e))})})]},e))})]}),{Title:eG,Text:ez}=f.Typography,eD=({entities:e,actions:t,selectedEntities:a,selectedActions:i,onEntitySelect:s,onActionSelect:n,entityCategories:o=[]})=>{let[d,c]=(0,r.useState)([]),m=new Map;o.forEach(e=>{e.entities.forEach(t=>{m.set(t,e.category)})});let u=e.filter(e=>0===d.length||d.includes(m.get(e)||""));return(0,l.jsxs)("div",{className:"pii-configuration",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-5",children:[(0,l.jsx)("div",{className:"flex items-center",children:(0,l.jsx)(eG,{level:4,className:"m-0! font-semibold text-gray-800",children:"Configure PII Protection"})}),(0,l.jsxs)(ez,{className:"text-gray-500",children:[a.length," items selected"]})]}),(0,l.jsxs)("div",{className:"mb-6",children:[(0,l.jsx)(eE,{categories:o,selectedCategories:d,onChange:c}),(0,l.jsx)(eM,{onSelectAll:t=>{e.forEach(e=>{a.includes(e)||s(e),n(e,t)})},onUnselectAll:()=>{a.forEach(e=>{s(e)})},hasSelectedEntities:a.length>0})]}),(0,l.jsx)(eR,{entities:u,selectedEntities:a,selectedActions:i,actions:t,onEntitySelect:s,onActionSelect:n,entityToCategoryMap:m})]})};var eK=e.i(304967),eq=e.i(599724),eH=e.i(312361),eU=e.i(21548),eJ=e.i(827252);let eW={rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""},eV=({value:e,onChange:t,disabled:a=!1})=>{let r={...eW,...e||{},rules:e?.rules?[...e.rules]:[]},s=e=>{let a={...r,...e};t?.(a)},n=(e,t)=>{s({rules:r.rules.map((a,l)=>l===e?{...a,...t}:a)})},o=(e,t)=>{let a=r.rules[e];if(!a)return;let l=Object.entries(a.allowed_param_patterns||{});t(l);let i={};l.forEach(([e,t])=>{i[e]=t}),n(e,{allowed_param_patterns:Object.keys(i).length>0?i:void 0})};return(0,l.jsxs)(eK.Card,{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"text-lg font-semibold",children:"LiteLLM Tool Permission Guardrail"}),(0,l.jsx)(eq.Text,{className:"text-sm text-gray-500",children:"Provide regex patterns (e.g., ^mcp__github_.*$) for tool names or types and optionally constrain payload fields."})]}),!a&&(0,l.jsx)(i.Button,{icon:(0,l.jsx)(d.PlusOutlined,{}),type:"primary",onClick:()=>{s({rules:[...r.rules,{id:`rule_${Math.random().toString(36).slice(2,8)}`,decision:"allow",allowed_param_patterns:void 0}]})},className:"bg-blue-600! text-white! hover:bg-blue-500!",children:"Add Rule"})]}),(0,l.jsx)(eH.Divider,{}),0===r.rules.length?(0,l.jsx)(eU.Empty,{description:"No tool rules added yet"}):(0,l.jsx)("div",{className:"space-y-4",children:r.rules.map((e,t)=>{let d;return(0,l.jsxs)(eK.Card,{className:"bg-gray-50",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,l.jsxs)(eq.Text,{className:"font-semibold",children:["Rule ",t+1]}),(0,l.jsx)(i.Button,{icon:(0,l.jsx)(L.DeleteOutlined,{}),danger:!0,type:"text",disabled:a,onClick:()=>{s({rules:r.rules.filter((e,a)=>a!==t)})},children:"Remove"})]}),(0,l.jsxs)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"text-sm font-medium",children:"Rule ID"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"unique_rule_id",value:e.id,onChange:e=>n(t,{id:e.target.value})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"text-sm font-medium",children:"Tool Name (optional)"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^mcp__github_.*$",value:e.tool_name??"",onChange:e=>n(t,{tool_name:""===e.target.value.trim()?void 0:e.target.value})})]})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 mt-4",children:(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"text-sm font-medium",children:"Tool Type (optional)"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^function$",value:e.tool_type??"",onChange:e=>n(t,{tool_type:""===e.target.value.trim()?void 0:e.target.value})})]})}),(0,l.jsxs)("div",{className:"mt-4 flex flex-col gap-2",children:[(0,l.jsx)(eq.Text,{className:"text-sm font-medium",children:"Decision"}),(0,l.jsxs)(x.Select,{disabled:a,value:e.decision,style:{width:200},onChange:e=>n(t,{decision:e}),children:[(0,l.jsx)(x.Select.Option,{value:"allow",children:"Allow"}),(0,l.jsx)(x.Select.Option,{value:"deny",children:"Deny"})]})]}),(0,l.jsx)("div",{className:"mt-4",children:0===(d=Object.entries(e.allowed_param_patterns||{})).length?(0,l.jsx)(i.Button,{disabled:a,size:"small",onClick:()=>n(t,{allowed_param_patterns:{"":""}}),children:"+ Restrict tool arguments (optional)"}):(0,l.jsxs)("div",{className:"space-y-2",children:[(0,l.jsx)(eq.Text,{className:"text-sm text-gray-600",children:"Argument constraints (dot or array paths)"}),d.map(([r,s],n)=>(0,l.jsxs)(j.Space,{align:"start",children:[(0,l.jsx)(p.Input,{disabled:a,placeholder:"messages[0].content",value:r,onChange:e=>{var a;return a=e.target.value,void o(t,e=>{if(!e[n])return;let[,t]=e[n];e[n]=[a,t]})}}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^email@.*$",value:s,onChange:e=>{var a;return a=e.target.value,void o(t,e=>{if(!e[n])return;let[t]=e[n];e[n]=[t,a]})}}),(0,l.jsx)(i.Button,{disabled:a,icon:(0,l.jsx)(L.DeleteOutlined,{}),danger:!0,onClick:()=>o(t,e=>{e.splice(n,1)})})]},`${e.id||t}-${n}`)),(0,l.jsx)(i.Button,{disabled:a,size:"small",onClick:()=>n(t,{allowed_param_patterns:{...e.allowed_param_patterns||{},"":""}}),children:"+ Add another constraint"})]})})]},e.id||t)})}),(0,l.jsx)(eH.Divider,{}),(0,l.jsxs)("div",{className:"grid gap-4 md:grid-cols-2",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"text-sm font-medium",children:"Default action"}),(0,l.jsxs)(x.Select,{disabled:a,value:r.default_action,onChange:e=>s({default_action:e}),children:[(0,l.jsx)(x.Select.Option,{value:"allow",children:"Allow"}),(0,l.jsx)(x.Select.Option,{value:"deny",children:"Deny"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)(eq.Text,{className:"text-sm font-medium flex items-center gap-1",children:["On disallowed action",(0,l.jsx)(eN.Tooltip,{title:"Block returns an error when a forbidden tool is invoked. Rewrite strips the tool call but lets the rest of the response continue.",children:(0,l.jsx)(eJ.InfoCircleOutlined,{})})]}),(0,l.jsxs)(x.Select,{disabled:a,value:r.on_disallowed_action,onChange:e=>s({on_disallowed_action:e}),children:[(0,l.jsx)(x.Select.Option,{value:"block",children:"Block"}),(0,l.jsx)(x.Select.Option,{value:"rewrite",children:"Rewrite"})]})]})]}),(0,l.jsxs)("div",{className:"mt-4",children:[(0,l.jsx)(eq.Text,{className:"text-sm font-medium",children:"Violation message (optional)"}),(0,l.jsx)(p.Input.TextArea,{disabled:a,rows:3,placeholder:"This violates our org policy...",value:r.violation_message_template,onChange:e=>s({violation_message_template:e.target.value})})]})]})},{Title:eY,Text:eQ,Link:eX}=f.Typography,{Option:eZ}=x.Select,e0={pre_call:"Before LLM Call - Runs before the LLM call and checks the input (Recommended)",during_call:"During LLM Call - Runs in parallel with the LLM call, with response held until check completes",post_call:"After LLM Call - Runs after the LLM call and checks only the output",logging_only:"Logging Only - Only runs on logging callbacks without affecting the LLM call",pre_mcp_call:"Before MCP Tool Call - Runs before MCP tool execution and validates tool calls",during_mcp_call:"During MCP Tool Call - Runs in parallel with MCP tool execution for monitoring"},e1=({visible:e,onClose:t,accessToken:a,onSuccess:s,preset:n})=>{let[o]=u.Form.useForm(),[d,c]=(0,r.useState)(!1),[f,j]=(0,r.useState)(null),[_,b]=(0,r.useState)(null),[v,w]=(0,r.useState)([]),[N,C]=(0,r.useState)({}),[S,k]=(0,r.useState)(0),[I,A]=(0,r.useState)(null),[O,T]=(0,r.useState)([]),[P,L]=(0,r.useState)(2),[B,F]=(0,r.useState)({}),[$,E]=(0,r.useState)([]),[M,R]=(0,r.useState)([]),[G,z]=(0,r.useState)([]),[D,K]=(0,r.useState)(""),[q,H]=(0,r.useState)(!1),[U,J]=(0,r.useState)(null),[W,V]=(0,r.useState)(""),[Y,Q]=(0,r.useState)(void 0),[X,Z]=(0,r.useState)("warn"),[ee,el]=(0,r.useState)(""),[er,eu]=(0,r.useState)(!1),[eg,ex]=(0,r.useState)([]),[eh,ef]=(0,r.useState)({rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""}),ey=(0,r.useMemo)(()=>!!f&&"tool_permission"===(en[f]||"").toLowerCase(),[f]);(0,r.useEffect)(()=>{a&&(async()=>{try{let[e,t,l]=await Promise.all([(0,m.getGuardrailUISettings)(a),(0,m.getGuardrailProviderSpecificParams)(a),(0,m.modelAvailableCall)(a,"","").catch(()=>null)]);b(e),A(t),l?.data&&ex(l.data.map(e=>e.id)),ei(t),eo(t)}catch(e){console.error("Error fetching guardrail data:",e),y.default.fromBackend("Failed to load guardrail configuration")}})()},[a]),(0,r.useEffect)(()=>{if(!n||!e||!_)return;j(n.provider);let t={provider:n.provider,guardrail_name:n.guardrailNameSuggestion,mode:n.mode,default_on:n.defaultOn,skip_system_message_choice:"inherit",skip_tool_message_choice:"inherit"};if("BlockCodeExecution"===n.provider&&(t.confidence_threshold=.5),o.setFieldsValue(t),n.categoryName&&_.content_filter_settings?.content_categories){let e=_.content_filter_settings.content_categories.find(e=>e.name===n.categoryName);e&&z([{id:`category-${Date.now()}`,category:e.name,display_name:e.display_name,action:e.default_action,severity_threshold:"medium"}])}},[n,e,_]);let ej=e=>{j(e);let t={config:void 0,presidio_analyzer_api_base:void 0,presidio_anonymizer_api_base:void 0};"BlockCodeExecution"===e&&(t.confidence_threshold=.5),o.setFieldsValue(t),w([]),C({}),T([]),L(2),F({}),E([]),R([]),z([]),K(""),H(!1),J(null),ef({rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""}),"LlmAsAJudge"===e&&o.setFieldsValue({mode:"post_call"})},eb=e=>{w(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},ev=(e,t)=>{C(a=>({...a,[e]:t}))},eN=async()=>{try{if(0===S&&(await o.validateFields(["guardrail_name","provider","mode","default_on"]),f)){let e=["guardrail_name","provider","mode","default_on"];"PresidioPII"===f&&e.push("presidio_analyzer_api_base","presidio_anonymizer_api_base"),await o.validateFields(e)}if(1===S&&ed(f)&&0===v.length)return void y.default.fromBackend("Please select at least one PII entity to continue");k(S+1)}catch(e){console.error("Form validation failed:",e)}},eC=()=>{o.resetFields(),j(null),w([]),C({}),T([]),L(2),F({}),E([]),R([]),z([]),K(""),ef({rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""}),V(""),Q(void 0),Z("warn"),el(""),eu(!1),k(0)},ek=()=>{eC(),t()},eI=async()=>{try{var e,l;c(!0),await o.validateFields();let r=o.getFieldsValue(!0),i=en[r.provider],n={guardrail_name:r.guardrail_name,litellm_params:{guardrail:i,mode:r.mode,default_on:r.default_on},guardrail_info:{}},d=(e=r.skip_system_message_choice,"yes"===e||"no"!==e&&void 0);void 0!==d&&(n.litellm_params.skip_system_message_in_guardrail=d);let u=(l=r.skip_tool_message_choice,"yes"===l||"no"!==l&&void 0);if(void 0!==u&&(n.litellm_params.skip_tool_message_in_guardrail=u),"PresidioPII"===r.provider&&v.length>0){let e={};v.forEach(t=>{e[t]=N[t]||"MASK"}),n.litellm_params.pii_entities_config=e,r.presidio_analyzer_api_base&&(n.litellm_params.presidio_analyzer_api_base=r.presidio_analyzer_api_base),r.presidio_anonymizer_api_base&&(n.litellm_params.presidio_anonymizer_api_base=r.presidio_anonymizer_api_base)}if(ec(r.provider)){let e=q&&U?.brand_self?.length>0;if(0===$.length&&0===M.length&&0===G.length&&!e){y.default.fromBackend("Please configure at least one content filter setting (category, pattern, keyword, or competitor intent)"),c(!1);return}$.length>0&&(n.litellm_params.patterns=$.map(e=>({pattern_type:"prebuilt"===e.type?"prebuilt":"regex",pattern_name:"prebuilt"===e.type?e.name:void 0,pattern:"custom"===e.type?e.pattern:void 0,name:e.name,action:e.action}))),M.length>0&&(n.litellm_params.blocked_words=M.map(e=>({keyword:e.keyword,action:e.action,description:e.description}))),G.length>0&&(n.litellm_params.categories=G.map(e=>({category:e.category,enabled:!0,action:e.action,severity_threshold:e.severity_threshold||"medium"}))),q&&U?.brand_self?.length>0&&(n.litellm_params.competitor_intent_config={competitor_intent_type:U.competitor_intent_type??"airline",brand_self:U.brand_self,locations:U.locations?.length>0?U.locations:void 0,competitors:"generic"===U.competitor_intent_type&&U.competitors?.length>0?U.competitors:void 0,policy:U.policy,threshold_high:U.threshold_high,threshold_medium:U.threshold_medium,threshold_low:U.threshold_low})}else if(r.config)try{n.guardrail_info=JSON.parse(r.config)}catch(e){y.default.fromBackend("Invalid JSON in configuration"),c(!1);return}if("llm_as_a_judge"===i){let e=r.criteria||[];if(0===e.length){y.default.fromBackend("Add at least one evaluation criterion"),c(!1);return}let t=e.reduce((e,t)=>e+(Number(t?.weight)||0),0);if(100!==t){y.default.fromBackend(`Criterion weights must sum to 100% (currently ${t}%)`),c(!1);return}n.litellm_params.judge_model=r.judge_model,n.litellm_params.overall_threshold=r.overall_threshold??80,n.litellm_params.on_failure=r.on_failure??"block",n.litellm_params.criteria=e.map(e=>({name:e.name,weight:Number(e.weight),description:e.description||""}))}if("tool_permission"===i){if(0===eh.rules.length){y.default.fromBackend("Add at least one tool permission rule"),c(!1);return}n.litellm_params.rules=eh.rules,n.litellm_params.default_action=eh.default_action,n.litellm_params.on_disallowed_action=eh.on_disallowed_action,eh.violation_message_template&&(n.litellm_params.violation_message_template=eh.violation_message_template)}if(ec(r.provider)&&(void 0!==Y&&Y>0&&(n.litellm_params.end_session_after_n_fails=Y),X&&"realtime"===W&&(n.litellm_params.on_violation=X),ee.trim()&&(n.litellm_params.realtime_violation_message=ee.trim())),I&&f&&"llm_as_a_judge"!==i){let e=I[en[f]?.toLowerCase()]||{},t=new Set;Object.keys(e).forEach(e=>{"optional_params"!==e&&t.add(e)}),e.optional_params&&e.optional_params.fields&&Object.keys(e.optional_params.fields).forEach(e=>{t.add(e)}),t.forEach(e=>{let t=r[e];(null==t||""===t)&&(t=r.optional_params?.[e]),null!=t&&""!==t&&(n.litellm_params[e]=t)})}if(!a)throw Error("No access token available");await (0,m.createGuardrailCall)(a,n),y.default.success("Guardrail created successfully"),eC(),s(),t()}catch(e){console.error("Failed to create guardrail:",e),y.default.fromBackend("Failed to create guardrail: "+(e instanceof Error?e.message:String(e)))}finally{c(!1)}},eA=e=>{if(!_||!ec(f))return null;let t=_.content_filter_settings;return t?(0,l.jsx)(et,{prebuiltPatterns:t.prebuilt_patterns||[],categories:t.pattern_categories||[],selectedPatterns:$,blockedWords:M,onPatternAdd:e=>E([...$,e]),onPatternRemove:e=>E($.filter(t=>t.id!==e)),onPatternActionChange:(e,t)=>{E($.map(a=>a.id===e?{...a,action:t}:a))},onBlockedWordAdd:e=>R([...M,e]),onBlockedWordRemove:e=>R(M.filter(t=>t.id!==e)),onBlockedWordUpdate:(e,t,a)=>{R(M.map(l=>l.id===e?{...l,[t]:a}:l))},contentCategories:t.content_categories||[],selectedContentCategories:G,onContentCategoryAdd:e=>z([...G,e]),onContentCategoryRemove:e=>z(G.filter(t=>t.id!==e)),onContentCategoryUpdate:(e,t,a)=>{z(G.map(l=>l.id===e?{...l,[t]:a}:l))},pendingCategorySelection:D,onPendingCategorySelectionChange:K,accessToken:a,showStep:e,competitorIntentEnabled:q,competitorIntentConfig:U,onCompetitorIntentChange:(e,t)=>{H(e),J(t)}}):null},eO=ec(f)?[{title:"Basic Info",optional:!1},{title:"Topics",optional:!1},{title:"Patterns",optional:!1},{title:"Keywords",optional:!1},{title:"Endpoint Settings (Optional)",optional:!0}]:ed(f)?[{title:"Basic Info",optional:!1},{title:"PII Configuration",optional:!1}]:[{title:"Basic Info",optional:!1},{title:"Provider Configuration",optional:!1}];return(0,l.jsx)(g.Modal,{title:null,open:e,onCancel:ek,maskClosable:!1,footer:null,width:1e3,closable:!1,className:"top-8",styles:{body:{padding:0}},children:(0,l.jsxs)("div",{className:"flex flex-col",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between px-6 py-4 border-b border-gray-200",children:[(0,l.jsx)("h3",{className:"text-base font-semibold text-gray-900 m-0",children:"Create guardrail"}),(0,l.jsx)("button",{onClick:ek,className:"text-gray-400 hover:text-gray-600 bg-transparent border-none cursor-pointer text-base leading-none p-1",children:"✕"})]}),(0,l.jsx)("div",{className:"overflow-auto px-6 py-4",style:{maxHeight:"calc(80vh - 120px)"},children:(0,l.jsx)(u.Form,{form:o,layout:"vertical",initialValues:{mode:"pre_call",default_on:!1,skip_system_message_choice:"inherit",skip_tool_message_choice:"inherit"},children:eO.map((e,t)=>{let r=t{r&&k(t)},style:{minHeight:24},children:[(0,l.jsx)("span",{className:"text-sm",style:{fontWeight:i?600:500,color:i?"#1e293b":r?"#4f46e5":"#94a3b8"},children:e.title}),e.optional&&!i&&(0,l.jsx)("span",{className:"text-[11px] text-slate-400",children:"optional"}),r&&(0,l.jsx)("span",{className:"text-[11px] text-indigo-500 hover:underline",children:"Edit"})]}),i&&(0,l.jsx)("div",{className:"mt-3",children:(()=>{switch(S){case 0:return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(u.Form.Item,{name:"guardrail_name",label:"Guardrail Name",rules:[{required:!0,message:"Please enter a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"Enter a name for this guardrail"})}),(0,l.jsx)(u.Form.Item,{name:"provider",label:"Guardrail Provider",rules:[{required:!0,message:"Please select a provider"}],children:(0,l.jsx)(x.Select,{placeholder:"Select a guardrail provider",onChange:ej,labelInValue:!1,optionLabelProp:"label",dropdownRender:e=>e,showSearch:!0,children:Object.entries(es()).map(([e,t])=>(0,l.jsx)(eZ,{value:e,label:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ep[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ep[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]}),children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ep[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ep[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]})},e))})}),(0,l.jsx)(u.Form.Item,{name:"mode",label:"Mode",tooltip:"How the guardrail should be applied",rules:[{required:!0,message:"Please select a mode"}],children:(0,l.jsx)(x.Select,{optionLabelProp:"label",mode:"multiple",children:_?.supported_modes?.map(e=>(0,l.jsx)(eZ,{value:e,label:e,children:(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:e}),"pre_call"===e&&(0,l.jsx)(h.Tag,{color:"green",style:{marginLeft:"8px"},children:"Recommended"})]}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e0[e]})]})},e))||(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eZ,{value:"pre_call",label:"pre_call",children:(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"pre_call"})," ",(0,l.jsx)(h.Tag,{color:"green",children:"Recommended"})]}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e0.pre_call})]})}),(0,l.jsx)(eZ,{value:"during_call",label:"during_call",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"during_call"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e0.during_call})]})}),(0,l.jsx)(eZ,{value:"post_call",label:"post_call",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"post_call"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e0.post_call})]})}),(0,l.jsx)(eZ,{value:"logging_only",label:"logging_only",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"logging_only"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e0.logging_only})]})})]})})}),(0,l.jsx)(u.Form.Item,{name:"default_on",label:"Always On",tooltip:"If enabled, this guardrail will be applied to all requests by default.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"Yes"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"No"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_system_message_choice",label:"Skip system messages in guardrail",tooltip:"Unified guardrails only: omit role: system from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_tool_message_choice",label:"Skip tool messages in guardrail",tooltip:"Unified guardrails only: omit role: tool from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),!ey&&!ec(f)&&!em(f)&&(0,l.jsx)(ew,{selectedProvider:f,accessToken:a,providerParams:I})]});case 1:if(ed(f))return _&&"PresidioPII"===f?(0,l.jsx)(eD,{entities:_.supported_entities,actions:_.supported_actions,selectedEntities:v,selectedActions:N,onEntitySelect:eb,onActionSelect:ev,entityCategories:_.pii_entity_categories}):null;if(ec(f))return eA("categories");if(em(f))return(0,l.jsx)(eS,{availableModels:eg,form:o});if(!f)return null;if(ey)return(0,l.jsx)(eV,{value:eh,onChange:ef});if(!I)return null;let e=en[f]?.toLowerCase(),t=I&&I[e];return t&&t.optional_params?(0,l.jsx)(e_,{optionalParams:t.optional_params,parentFieldKey:"optional_params"}):null;case 2:if(ec(f))return eA("patterns");return null;case 3:if(ec(f))return eA("keywords");return null;case 4:return(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsx)("div",{children:(0,l.jsxs)("p",{className:"text-sm text-gray-500",children:["Configure settings for a specific call type. Most guardrails don't need this — skip it unless you're using a specific endpoint like ",(0,l.jsx)("code",{children:"/v1/realtime"}),"."]})}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Call type"}),(0,l.jsx)(x.Select,{placeholder:"Select a call type",value:W||void 0,onChange:e=>{V(e),eu(!1)},style:{width:260},allowClear:!0,options:[{value:"realtime",label:"/v1/realtime"}]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"More call types coming soon."})]}),"realtime"===W&&(0,l.jsxs)("div",{className:"border border-gray-200 rounded-lg overflow-hidden",children:[(0,l.jsxs)("button",{type:"button",onClick:()=>eu(e=>!e),className:"w-full flex items-center justify-between px-4 py-3 bg-gray-50 hover:bg-gray-100 text-sm font-medium text-gray-700",children:[(0,l.jsx)("span",{children:"/v1/realtime settings"}),(0,l.jsx)("svg",{className:`w-4 h-4 text-gray-500 transition-transform ${er?"rotate-180":""}`,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,l.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})})]}),er&&(0,l.jsxs)("div",{className:"space-y-5 px-4 py-4 border-t border-gray-200",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"End session after X violations"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Automatically close the session after this many guardrail violations. Leave empty to never auto-close."}),(0,l.jsx)("input",{type:"number",min:1,placeholder:"e.g. 3",value:Y??"",onChange:e=>Q(e.target.value?parseInt(e.target.value,10):void 0),className:"border border-gray-300 rounded-sm px-3 py-1.5 text-sm w-32"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"On violation"}),(0,l.jsx)("div",{className:"space-y-2",children:["warn","end_session"].map(e=>(0,l.jsxs)("label",{className:"flex items-start gap-2 cursor-pointer",children:[(0,l.jsx)("input",{type:"radio",name:"on_violation",value:e,checked:X===e,onChange:()=>Z(e),className:"mt-0.5"}),(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"text-sm font-medium text-gray-800",children:"warn"===e?"Warn":"End session"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 m-0",children:"warn"===e?"Bot speaks the message, session continues":"Bot speaks the message, connection closes immediately"})]})]},e))})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Message the user hears"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"What the bot says aloud when this guardrail fires. Falls back to the default violation message if empty."}),(0,l.jsx)("textarea",{rows:3,placeholder:"e.g. I'm not able to continue this conversation. Please contact us at 1-800-774-2678.",value:ee,onChange:e=>el(e.target.value),className:"border border-gray-300 rounded-sm px-3 py-2 text-sm w-full resize-none"})]})]})]})]});default:return null}})()})]})]},t)})})}),(0,l.jsxs)("div",{className:"flex items-center justify-end space-x-3 px-6 py-3 border-t border-gray-200",children:[(0,l.jsx)(i.Button,{onClick:ek,children:"Cancel"}),S>0&&(0,l.jsx)(i.Button,{onClick:()=>{k(S-1)},children:"Previous"}),S{let[d]=u.Form.useForm(),[c,h]=(0,r.useState)(!1),[f,j]=(0,r.useState)(o?.provider||null),[_,b]=(0,r.useState)(null),[v,w]=(0,r.useState)([]),[N,C]=(0,r.useState)({});(0,r.useEffect)(()=>{(async()=>{try{if(!a)return;let e=await (0,m.getGuardrailUISettings)(a);b(e)}catch(e){console.error("Error fetching guardrail settings:",e),y.default.fromBackend("Failed to load guardrail settings")}})()},[a]),(0,r.useEffect)(()=>{o?.pii_entities_config&&Object.keys(o.pii_entities_config).length>0&&(w(Object.keys(o.pii_entities_config)),C(o.pii_entities_config))},[o]);let S=e=>{w(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},k=(e,t)=>{C(a=>({...a,[e]:t}))},I=async()=>{try{h(!0);let e=await d.validateFields(),l=en[e.provider],r=n&&"object"==typeof n?{...n}:{};r.guardrail=l,r.mode=e.mode,r.default_on=e.default_on;let o=e.skip_system_message_choice;"yes"===o?r.skip_system_message_in_guardrail=!0:"no"===o?r.skip_system_message_in_guardrail=!1:delete r.skip_system_message_in_guardrail;let c=e.skip_tool_message_choice;"yes"===c?r.skip_tool_message_in_guardrail=!0:"no"===c?r.skip_tool_message_in_guardrail=!1:delete r.skip_tool_message_in_guardrail;let u={};if("PresidioPII"===e.provider&&v.length>0){let e={};v.forEach(t=>{e[t]=N[t]||"MASK"}),r.pii_entities_config=e}else if(e.config)try{let t=JSON.parse(e.config);"Bedrock"===e.provider&&t?(t.guardrail_id&&(r.guardrailIdentifier=t.guardrail_id),t.guardrail_version&&(r.guardrailVersion=t.guardrail_version)):u=t}catch(e){y.default.fromBackend("Invalid JSON in configuration"),h(!1);return}let p={guardrail_id:s,guardrail:{guardrail_name:e.guardrail_name,litellm_params:r,guardrail_info:u}};if(!a)throw Error("No access token available");let g=`/guardrails/${s}`,x=await fetch(g,{method:"PUT",headers:{[(0,m.getGlobalLitellmHeaderName)()]:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify(p)});if(!x.ok){let e=await x.text();throw Error(e||"Failed to update guardrail")}y.default.success("Guardrail updated successfully"),i(),t()}catch(e){console.error("Failed to update guardrail:",e),y.default.fromBackend("Failed to update guardrail: "+(e instanceof Error?e.message:String(e)))}finally{h(!1)}};return(0,l.jsx)(g.Modal,{title:"Edit Guardrail",open:e,onCancel:t,footer:null,width:700,children:(0,l.jsxs)(u.Form,{form:d,layout:"vertical",initialValues:o,children:[(0,l.jsx)(u.Form.Item,{name:"guardrail_name",label:"Guardrail Name",rules:[{required:!0,message:"Please enter a guardrail name"}],children:(0,l.jsx)(tn.TextInput,{placeholder:"Enter a name for this guardrail"})}),(0,l.jsx)(u.Form.Item,{name:"provider",label:"Guardrail Provider",rules:[{required:!0,message:"Please select a provider"}],children:(0,l.jsx)(x.Select,{placeholder:"Select a guardrail provider",onChange:e=>{j(e),d.setFieldsValue({config:void 0}),w([]),C({})},disabled:!0,optionLabelProp:"label",children:Object.entries(es()).map(([e,t])=>(0,l.jsx)(tc,{value:e,label:t,children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ep[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ep[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]})},e))})}),(0,l.jsx)(u.Form.Item,{name:"mode",label:"Mode",tooltip:"How the guardrail should be applied",rules:[{required:!0,message:"Please select a mode"}],children:(0,l.jsx)(x.Select,{children:_?.supported_modes?.map(e=>(0,l.jsx)(tc,{value:e,children:e},e))||(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tc,{value:"pre_call",children:"pre_call"}),(0,l.jsx)(tc,{value:"post_call",children:"post_call"})]})})}),(0,l.jsx)(u.Form.Item,{name:"default_on",label:"Always On",tooltip:"If enabled, this guardrail will be applied to all requests by default",valuePropName:"checked",children:(0,l.jsx)(U.Switch,{})}),(0,l.jsx)(u.Form.Item,{name:"skip_system_message_choice",label:"Skip system messages in guardrail",tooltip:"Unified guardrails only: whether role: system content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(tc,{value:"inherit",children:"Use global default"}),(0,l.jsx)(tc,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(tc,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_tool_message_choice",label:"Skip tool messages in guardrail",tooltip:"Unified guardrails only: whether role: tool content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(tc,{value:"inherit",children:"Use global default"}),(0,l.jsx)(tc,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(tc,{value:"no",children:"No — always include in scan"})]})}),(()=>{if(!f)return null;if("PresidioPII"===f)return _&&f&&"PresidioPII"===f?(0,l.jsx)(eD,{entities:_.supported_entities,actions:_.supported_actions,selectedEntities:v,selectedActions:N,onEntitySelect:S,onActionSelect:k,entityCategories:_.pii_entity_categories}):null;switch(f){case"Aporia":return(0,l.jsx)(u.Form.Item,{label:"Aporia Configuration",name:"config",tooltip:"JSON configuration for Aporia",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,509345,e=>{"use strict";var t,a,l=e.i(843476),r=e.i(271645),i=e.i(464571),s=e.i(326373),n=e.i(653496),o=e.i(755151),d=e.i(646563),c=e.i(245094),m=e.i(602869),u=e.i(808613),p=e.i(311451),g=e.i(212931),x=e.i(199133),h=e.i(262218),f=e.i(898586),y=e.i(727749),j=e.i(770914),_=e.i(515831),b=e.i(175712),v=e.i(519756);let{Text:w}=f.Typography,{Option:C}=x.Select,N=({visible:e,prebuiltPatterns:t,categories:a,selectedPatternName:r,patternAction:s,onPatternNameChange:n,onActionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add prebuilt pattern",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(w,{strong:!0,children:"Pattern type"}),(0,l.jsx)(x.Select,{placeholder:"Choose pattern type",value:r,onChange:n,style:{width:"100%",marginTop:8},showSearch:!0,filterOption:(e,a)=>{let l=t.find(e=>e.name===a?.value);return!!l&&(l.display_name.toLowerCase().includes(e.toLowerCase())||l.name.toLowerCase().includes(e.toLowerCase()))},children:a.map(e=>{let a=t.filter(t=>t.category===e);return 0===a.length?null:(0,l.jsx)(x.Select.OptGroup,{label:e,children:a.map(e=>(0,l.jsx)(C,{value:e.name,children:e.display_name},e.name))},e)})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(w,{strong:!0,children:"Action"}),(0,l.jsx)(w,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this pattern is detected"}),(0,l.jsxs)(x.Select,{value:s,onChange:o,style:{width:"100%"},children:[(0,l.jsx)(C,{value:"BLOCK",children:"Block"}),(0,l.jsx)(C,{value:"MASK",children:"Mask"})]})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]}),{Text:k}=f.Typography,{Option:S}=x.Select,I=({visible:e,patternName:t,patternRegex:a,patternAction:r,onNameChange:s,onRegexChange:n,onActionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add custom regex pattern",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(k,{strong:!0,children:"Pattern name"}),(0,l.jsx)(p.Input,{placeholder:"e.g., internal_id, employee_code",value:t,onChange:e=>s(e.target.value),style:{marginTop:8}})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(k,{strong:!0,children:"Regex pattern"}),(0,l.jsx)(p.Input,{placeholder:"e.g., ID-[0-9]{6}",value:a,onChange:e=>n(e.target.value),style:{marginTop:8}}),(0,l.jsx)(k,{type:"secondary",style:{fontSize:12},children:"Enter a valid regular expression to match sensitive data"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(k,{strong:!0,children:"Action"}),(0,l.jsx)(k,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this pattern is detected"}),(0,l.jsxs)(x.Select,{value:r,onChange:o,style:{width:"100%"},children:[(0,l.jsx)(S,{value:"BLOCK",children:"Block"}),(0,l.jsx)(S,{value:"MASK",children:"Mask"})]})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]}),{Text:A}=f.Typography,{Option:O}=x.Select,T=({visible:e,keyword:t,action:a,description:r,onKeywordChange:s,onActionChange:n,onDescriptionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add blocked keyword",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Keyword"}),(0,l.jsx)(p.Input,{placeholder:"Enter sensitive keyword or phrase",value:t,onChange:e=>s(e.target.value),style:{marginTop:8}})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Action"}),(0,l.jsx)(A,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this keyword is detected"}),(0,l.jsxs)(x.Select,{value:a,onChange:n,style:{width:"100%"},children:[(0,l.jsx)(O,{value:"BLOCK",children:"Block"}),(0,l.jsx)(O,{value:"MASK",children:"Mask"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Description (optional)"}),(0,l.jsx)(p.Input.TextArea,{placeholder:"Explain why this keyword is sensitive",value:r,onChange:e=>o(e.target.value),rows:3,style:{marginTop:8}})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]});var P=e.i(291542),L=e.i(955135);let{Text:B}=f.Typography,{Option:F}=x.Select,$=({patterns:e,onActionChange:t,onRemove:a})=>{let r=[{title:"Type",dataIndex:"type",key:"type",width:100,render:e=>(0,l.jsx)(h.Tag,{color:"prebuilt"===e?"blue":"green",children:"prebuilt"===e?"Prebuilt":"Custom"})},{title:"Pattern name",dataIndex:"name",key:"name",render:(e,t)=>t.display_name||t.name},{title:"Regex pattern",dataIndex:"pattern",key:"pattern",render:e=>e?(0,l.jsxs)(B,{code:!0,style:{fontSize:12},children:[e.substring(0,40),"..."]}):"-"},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>t(a.id,e),style:{width:120},size:"small",children:[(0,l.jsx)(F,{value:"BLOCK",children:"Block"}),(0,l.jsx)(F,{value:"MASK",children:"Mask"})]})},{title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>a(t.id),children:"Delete"})}];return 0===e.length?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No patterns added."}):(0,l.jsx)(P.Table,{dataSource:e,columns:r,rowKey:"id",pagination:!1,size:"small"})},{Text:E}=f.Typography,{Option:M}=x.Select,R=({keywords:e,onActionChange:t,onRemove:a})=>{let r=[{title:"Keyword",dataIndex:"keyword",key:"keyword"},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>t(a.id,"action",e),style:{width:120},size:"small",children:[(0,l.jsx)(M,{value:"BLOCK",children:"Block"}),(0,l.jsx)(M,{value:"MASK",children:"Mask"})]})},{title:"Description",dataIndex:"description",key:"description",render:e=>e||"-"},{title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>a(t.id),children:"Delete"})}];return 0===e.length?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No keywords added."}):(0,l.jsx)(P.Table,{dataSource:e,columns:r,rowKey:"id",pagination:!1,size:"small"})};var G=e.i(362024),z=e.i(993914);let{Title:D,Text:K}=f.Typography,{Option:q}=x.Select,H=({availableCategories:e,selectedCategories:t,onCategoryAdd:a,onCategoryRemove:s,onCategoryUpdate:n,accessToken:o,pendingSelection:c,onPendingSelectionChange:u})=>{let[p,g]=r.default.useState(""),f=void 0!==c?c:p,y=u||g,[j,_]=r.default.useState({}),[v,w]=r.default.useState({}),[C,N]=r.default.useState({}),[k,S]=r.default.useState([]),[I,A]=r.default.useState(""),[O,T]=r.default.useState(!1),B=async e=>{if(o&&!j[e]){N(t=>({...t,[e]:!0}));try{let t=await (0,m.getCategoryYaml)(o,e),a=t.yaml_content;if("json"===t.file_type)try{let e=JSON.parse(a);a=JSON.stringify(e,null,2)}catch(t){console.warn(`Failed to format JSON for ${e}:`,t)}_(t=>({...t,[e]:a})),w(a=>({...a,[e]:t.file_type||"yaml"}))}catch(t){console.error(`Failed to fetch content for category ${e}:`,t)}finally{N(t=>({...t,[e]:!1}))}}};r.default.useEffect(()=>{if(f&&o){let e=j[f];if(e)return void A(e);T(!0),(0,m.getCategoryYaml)(o,f).then(e=>{let t=e.yaml_content;if("json"===e.file_type)try{let e=JSON.parse(t);t=JSON.stringify(e,null,2)}catch(e){console.warn(`Failed to format JSON for ${f}:`,e)}A(t),_(e=>({...e,[f]:t})),w(t=>({...t,[f]:e.file_type||"yaml"}))}).catch(e=>{console.error(`Failed to fetch preview content for category ${f}:`,e),A("")}).finally(()=>{T(!1)})}else A(""),T(!1)},[f,o]);let F=[{title:"Category",dataIndex:"display_name",key:"display_name",render:(t,a)=>{let r=e.find(e=>e.name===a.category);return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{fontWeight:500},children:t}),r?.description&&(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888",marginTop:"4px"},children:r.description})]})}},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,t)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>n(t.id,"action",e),style:{width:"100%"},children:[(0,l.jsx)(q,{value:"BLOCK",children:(0,l.jsx)(h.Tag,{color:"red",children:"BLOCK"})}),(0,l.jsx)(q,{value:"MASK",children:(0,l.jsx)(h.Tag,{color:"orange",children:"MASK"})})]})},{title:"Severity Threshold",dataIndex:"severity_threshold",key:"severity_threshold",width:180,render:(e,t)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>n(t.id,"severity_threshold",e),style:{width:"100%"},children:[(0,l.jsx)(q,{value:"low",children:"Low"}),(0,l.jsx)(q,{value:"medium",children:"Medium"}),(0,l.jsx)(q,{value:"high",children:"High"})]})},{title:"",key:"actions",width:80,render:(e,t)=>(0,l.jsx)(i.Button,{icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>s(t.id),size:"small",children:"Remove"})}],$=e.filter(e=>!t.some(t=>t.category===e.name));return(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:8},children:[(0,l.jsx)(D,{level:5,style:{margin:0},children:"Blocked topics"}),(0,l.jsx)(K,{type:"secondary",style:{fontSize:12,fontWeight:400},children:"Select topics to block using keyword and semantic analysis"})]}),size:"small",children:[(0,l.jsxs)("div",{style:{marginBottom:16,display:"flex",gap:8},children:[(0,l.jsx)(x.Select,{placeholder:"Select a content category",value:f||void 0,onChange:y,style:{flex:1},showSearch:!0,optionLabelProp:"label",filterOption:(e,t)=>(t?.label?.toString().toLowerCase()??"").includes(e.toLowerCase()),children:$.map(e=>(0,l.jsx)(q,{value:e.name,label:e.display_name,children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{fontWeight:500},children:e.display_name}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#666",marginTop:"2px"},children:e.description})]})},e.name))}),(0,l.jsx)(i.Button,{type:"primary",onClick:()=>{if(!f)return;let l=e.find(e=>e.name===f);!l||t.some(e=>e.category===f)||(a({id:`category-${Date.now()}`,category:l.name,display_name:l.display_name,action:l.default_action,severity_threshold:"medium"}),y(""),A(""))},disabled:!f,icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add"})]}),f&&(0,l.jsxs)("div",{style:{marginBottom:16,padding:"12px",background:"#f9f9f9",border:"1px solid #e0e0e0",borderRadius:"4px"},children:[(0,l.jsxs)("div",{style:{marginBottom:8,fontWeight:500,fontSize:"14px"},children:["Preview: ",e.find(e=>e.name===f)?.display_name,v[f]&&(0,l.jsxs)("span",{style:{marginLeft:8,fontSize:"12px",color:"#888",fontWeight:400},children:["(",v[f]?.toUpperCase(),")"]})]}),O?(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Loading content..."}):I?(0,l.jsx)("pre",{style:{background:"#fff",padding:"12px",borderRadius:"4px",overflow:"auto",maxHeight:"300px",maxWidth:"100%",fontSize:"12px",lineHeight:"1.5",margin:0,border:"1px solid #e0e0e0",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:(0,l.jsx)("code",{children:I})}):(0,l.jsx)("div",{style:{padding:"8px",textAlign:"center",color:"#888",fontSize:"12px"},children:"Unable to load category content"})]}),t.length>0?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(P.Table,{dataSource:t,columns:F,pagination:!1,size:"small",rowKey:"id"}),(0,l.jsx)("div",{style:{marginTop:16},children:(0,l.jsx)(G.Collapse,{activeKey:k,onChange:e=>{let t=Array.isArray(e)?e:e?[e]:[],a=new Set(k);t.forEach(e=>{a.has(e)||j[e]||B(e)}),S(t)},ghost:!0,items:t.map(e=>{let t=(v[e.category]||"yaml").toUpperCase();return{key:e.category,label:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,l.jsx)(z.FileTextOutlined,{}),(0,l.jsxs)("span",{children:["View ",t," for ",e.display_name]})]}),children:C[e.category]?(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Loading content..."}):j[e.category]?(0,l.jsx)("pre",{style:{background:"#f5f5f5",padding:"16px",borderRadius:"4px",overflow:"auto",maxHeight:"400px",fontSize:"12px",lineHeight:"1.5",margin:0},children:(0,l.jsx)("code",{children:j[e.category]})}):(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Content will load when expanded"})}})})})]}):(0,l.jsx)("div",{style:{textAlign:"center",padding:"24px",color:"#888",border:"1px dashed #d9d9d9",borderRadius:"4px"},children:"No blocked topics selected. Add topics to detect and block harmful content."})]})};var U=e.i(790848),J=e.i(28651);let{Title:W,Text:V}=f.Typography,{Option:Y}=x.Select,Q={competitor_intent_type:"airline",brand_self:[],locations:[],policy:{competitor_comparison:"refuse",possible_competitor_comparison:"reframe"},threshold_high:.7,threshold_medium:.45,threshold_low:.3},X=({enabled:e,config:t,onChange:a,accessToken:i})=>{let s=t??Q,[n,o]=(0,r.useState)([]),[d,c]=(0,r.useState)(!1);(0,r.useEffect)(()=>{"airline"===s.competitor_intent_type&&i&&0===n.length&&(c(!0),(0,m.getMajorAirlines)(i).then(e=>o(e.airlines??[])).catch(()=>o([])).finally(()=>c(!1)))},[s.competitor_intent_type,i,n.length]);let p=e=>{a(e,e?{...Q}:null)},g=(t,l)=>{a(e,{...s,[t]:l})},h=(t,l)=>{a(e,{...s,policy:{...s.policy,[t]:l}})},f=(t,l)=>{a(e,{...s,[t]:l.filter(Boolean)})};return e?(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(W,{level:5,style:{margin:0},children:"Competitor Intent Filter"}),(0,l.jsx)(U.Switch,{checked:e,onChange:p})]}),size:"small",children:[(0,l.jsx)(V,{type:"secondary",style:{display:"block",marginBottom:16},children:"Block or reframe competitor comparison questions. Airline type uses major airlines (excluding your brand); generic requires manual competitor list."}),(0,l.jsxs)(u.Form,{layout:"vertical",size:"small",children:[(0,l.jsx)(u.Form.Item,{label:"Type",children:(0,l.jsxs)(x.Select,{value:s.competitor_intent_type,onChange:e=>g("competitor_intent_type",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"airline",children:"Airline (auto-load competitors from IATA)"}),(0,l.jsx)(Y,{value:"generic",children:"Generic (specify competitors manually)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Your Brand (brand_self)",required:!0,help:"airline"===s.competitor_intent_type?"Select your airline from the list (excluded from competitors) or type to add a custom term":"Names/codes users use for your brand",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:d?"Loading airlines...":"airline"===s.competitor_intent_type?"Search or select airline, or type to add custom":"Type and press Enter to add",value:s.brand_self,onChange:t=>"airline"===s.competitor_intent_type&&n.length>0?(t=>{let l=t.filter(Boolean),r=[],i=new Set;for(let e of l){let t=n.find(t=>t.match.split("|")[0]?.trim().toLowerCase()===e.toLowerCase());if(t)for(let e of t.match.split("|").map(e=>e.trim().toLowerCase()).filter(Boolean))i.has(e)||(i.add(e),r.push(e));else i.has(e.toLowerCase())||(i.add(e.toLowerCase()),r.push(e))}a(e,{...s,brand_self:r})})(t??[]):f("brand_self",t??[]),tokenSeparators:[","],loading:d,showSearch:!0,filterOption:(e,t)=>(t?.label?.toString().toLowerCase()??"").includes(e.toLowerCase()),optionFilterProp:"label",options:"airline"===s.competitor_intent_type&&n.length>0?n.map(e=>{let t=e.match.split("|")[0]?.trim()??e.id,a=e.match.split("|").map(e=>e.trim().toLowerCase()).filter(Boolean);return{value:t.toLowerCase(),label:`${t}${a.length>1?` (${a.slice(1).join(", ")})`:""}`}}):void 0})}),"airline"===s.competitor_intent_type&&(0,l.jsx)(u.Form.Item,{label:"Locations (optional)",help:"Countries, cities, airports for disambiguation (e.g. qatar, doha)",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type and press Enter to add",value:s.locations??[],onChange:e=>f("locations",e??[]),tokenSeparators:[","]})}),"generic"===s.competitor_intent_type&&(0,l.jsx)(u.Form.Item,{label:"Competitors",required:!0,help:"Competitor names to detect (required for generic type)",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type and press Enter to add",value:s.competitors??[],onChange:e=>f("competitors",e??[]),tokenSeparators:[","]})}),(0,l.jsx)(u.Form.Item,{label:"Policy: Competitor comparison",children:(0,l.jsxs)(x.Select,{value:s.policy?.competitor_comparison??"refuse",onChange:e=>h("competitor_comparison",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"refuse",children:"Refuse (block request)"}),(0,l.jsx)(Y,{value:"reframe",children:"Reframe (suggest alternative)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Policy: Possible competitor comparison",children:(0,l.jsxs)(x.Select,{value:s.policy?.possible_competitor_comparison??"reframe",onChange:e=>h("possible_competitor_comparison",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"refuse",children:"Refuse (block request)"}),(0,l.jsx)(Y,{value:"reframe",children:"Reframe (suggest alternative to backend LLM)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Confidence thresholds",help:(0,l.jsxs)(l.Fragment,{children:["Classify competitor intent by confidence (0–1). Higher confidence → stronger intent.",(0,l.jsxs)("ul",{style:{marginBottom:0,marginTop:4,paddingLeft:20},children:[(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"High (≥)"}),': Treat as full competitor comparison → uses "Competitor comparison" policy']}),(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"Medium (≥)"}),': Treat as possible comparison → uses "Possible competitor comparison" policy']}),(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"Low (≥)"}),": Log only; allow request. Below Low → allow with no action"]})]}),"Raise thresholds to be more permissive; lower them to be stricter."]}),children:(0,l.jsxs)(j.Space,{wrap:!0,children:[(0,l.jsx)(u.Form.Item,{label:"High",style:{marginBottom:0},help:"e.g. 0.7",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_high??.7,onChange:e=>g("threshold_high",e??.7),style:{width:80}})}),(0,l.jsx)(u.Form.Item,{label:"Medium",style:{marginBottom:0},help:"e.g. 0.45",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_medium??.45,onChange:e=>g("threshold_medium",e??.45),style:{width:80}})}),(0,l.jsx)(u.Form.Item,{label:"Low",style:{marginBottom:0},help:"e.g. 0.3",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_low??.3,onChange:e=>g("threshold_low",e??.3),style:{width:80}})})]})})]})]}):(0,l.jsx)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(W,{level:5,style:{margin:0},children:"Competitor Intent Filter"}),(0,l.jsx)(U.Switch,{checked:!1,onChange:p})]}),size:"small",children:(0,l.jsx)(V,{type:"secondary",children:"Block or reframe competitor comparison questions. When enabled, airline type auto-loads competitors from IATA; generic type requires manual competitor list."})})},{Title:Z,Text:ee}=f.Typography,et=({prebuiltPatterns:e,categories:t,selectedPatterns:a,blockedWords:s,onPatternAdd:n,onPatternRemove:o,onPatternActionChange:c,onBlockedWordAdd:u,onBlockedWordRemove:p,onBlockedWordUpdate:g,onFileUpload:x,accessToken:h,showStep:f,contentCategories:w=[],selectedContentCategories:C=[],onContentCategoryAdd:k,onContentCategoryRemove:S,onContentCategoryUpdate:A,pendingCategorySelection:O,onPendingCategorySelectionChange:P,competitorIntentEnabled:L=!1,competitorIntentConfig:B=null,onCompetitorIntentChange:F})=>{let[E,M]=(0,r.useState)(!1),[G,z]=(0,r.useState)(!1),[D,K]=(0,r.useState)(!1),[q,U]=(0,r.useState)(""),[J,W]=(0,r.useState)("BLOCK"),[V,Y]=(0,r.useState)(""),[Q,et]=(0,r.useState)(""),[ea,el]=(0,r.useState)("BLOCK"),[er,ei]=(0,r.useState)(""),[es,en]=(0,r.useState)("BLOCK"),[eo,ed]=(0,r.useState)(""),[ec,em]=(0,r.useState)(!1),eu=async e=>{em(!0);try{let t=await e.text();if(h){let e=await (0,m.validateBlockedWordsFile)(h,t);if(e.valid)x&&x(t),y.default.success(e.message||"File uploaded successfully");else{let t=e.error||e.errors&&e.errors.join(", ")||"Invalid file";y.default.error(`Validation failed: ${t}`)}}}catch(e){y.default.error(`Failed to upload file: ${e}`)}finally{em(!1)}return!1};return(0,l.jsxs)("div",{className:"space-y-6",children:[!f&&(0,l.jsx)("div",{children:(0,l.jsx)(ee,{type:"secondary",children:"Configure patterns, keywords, and content categories to detect and filter sensitive information in requests and responses."})}),(!f||"patterns"===f)&&(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(Z,{level:5,style:{margin:0},children:"Pattern Detection"}),(0,l.jsx)(ee,{type:"secondary",style:{fontSize:14,fontWeight:400},children:"Detect sensitive information using regex patterns (SSN, credit cards, API keys, etc.)"})]}),size:"small",children:[(0,l.jsx)("div",{style:{marginBottom:16},children:(0,l.jsxs)(j.Space,{children:[(0,l.jsx)(i.Button,{type:"primary",onClick:()=>M(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add prebuilt pattern"}),(0,l.jsx)(i.Button,{onClick:()=>K(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add custom regex"})]})}),(0,l.jsx)($,{patterns:a,onActionChange:c,onRemove:o})]}),(!f||"keywords"===f)&&(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(Z,{level:5,style:{margin:0},children:"Blocked Keywords"}),(0,l.jsx)(ee,{type:"secondary",style:{fontSize:14,fontWeight:400},children:"Block or mask specific sensitive terms and phrases"})]}),size:"small",children:[(0,l.jsx)("div",{style:{marginBottom:16},children:(0,l.jsxs)(j.Space,{children:[(0,l.jsx)(i.Button,{type:"primary",onClick:()=>z(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add keyword"}),(0,l.jsx)(_.Upload,{beforeUpload:eu,accept:".yaml,.yml",showUploadList:!1,children:(0,l.jsx)(i.Button,{icon:(0,l.jsx)(v.UploadOutlined,{}),loading:ec,children:"Upload YAML file"})})]})}),(0,l.jsx)(R,{keywords:s,onActionChange:g,onRemove:p})]}),(!f||"competitor_intent"===f||"categories"===f)&&F&&(0,l.jsx)(X,{enabled:L,config:B,onChange:F,accessToken:h}),(!f||"categories"===f)&&w.length>0&&k&&S&&A&&(0,l.jsx)(H,{availableCategories:w,selectedCategories:C,onCategoryAdd:k,onCategoryRemove:S,onCategoryUpdate:A,accessToken:h,pendingSelection:O,onPendingSelectionChange:P}),(0,l.jsx)(N,{visible:E,prebuiltPatterns:e,categories:t,selectedPatternName:q,patternAction:J,onPatternNameChange:U,onActionChange:e=>W(e),onAdd:()=>{if(!q)return void y.default.error("Please select a pattern");let t=e.find(e=>e.name===q);n({id:`pattern-${Date.now()}`,type:"prebuilt",name:q,display_name:t?.display_name,action:J}),M(!1),U(""),W("BLOCK")},onCancel:()=>{M(!1),U(""),W("BLOCK")}}),(0,l.jsx)(I,{visible:D,patternName:V,patternRegex:Q,patternAction:ea,onNameChange:Y,onRegexChange:et,onActionChange:e=>el(e),onAdd:()=>{V&&Q?(n({id:`custom-${Date.now()}`,type:"custom",name:V,pattern:Q,action:ea}),K(!1),Y(""),et(""),el("BLOCK")):y.default.error("Please provide pattern name and regex")},onCancel:()=>{K(!1),Y(""),et(""),el("BLOCK")}}),(0,l.jsx)(T,{visible:G,keyword:er,action:es,description:eo,onKeywordChange:ei,onActionChange:e=>en(e),onDescriptionChange:ed,onAdd:()=>{er?(u({id:`word-${Date.now()}`,keyword:er,action:es,description:eo||void 0}),z(!1),ei(""),ed(""),en("BLOCK")):y.default.error("Please enter a keyword")},onCancel:()=>{z(!1),ei(""),ed(""),en("BLOCK")}})]})};var ea=e.i(555987),el=((t={}).PresidioPII="Presidio PII",t.Bedrock="Bedrock Guardrail",t.Lakera="Lakera",t);let er={},ei=e=>{let t={};return t.PresidioPII="Presidio PII",t.Bedrock="Bedrock Guardrail",t.Lakera="Lakera",t.LlmAsAJudge="LiteLLM LLM as a Judge",Object.entries(e).forEach(([e,a])=>{a&&"object"==typeof a&&"ui_friendly_name"in a&&(t[e.split("_").map((e,t)=>e.charAt(0).toUpperCase()+e.slice(1)).join("")]=a.ui_friendly_name)}),er=t,t},es=()=>Object.keys(er).length>0?er:el,en={PresidioPII:"presidio",Bedrock:"bedrock",Lakera:"lakera_v2",LitellmContentFilter:"litellm_content_filter",ToolPermission:"tool_permission",BlockCodeExecution:"block_code_execution",Promptguard:"promptguard",LlmAsAJudge:"llm_as_a_judge",Xecguard:"xecguard",QostodianNexus:"qostodian_nexus",Repelloai:"repelloai"},eo=e=>{Object.entries(e).forEach(([e,t])=>{t&&"object"==typeof t&&"ui_friendly_name"in t&&(en[e.split("_").map((e,t)=>e.charAt(0).toUpperCase()+e.slice(1)).join("")]=e)})},ed=e=>Array.isArray(e)?e.filter(e=>"string"==typeof e):"string"==typeof e?[e]:[],ec=(e,t)=>{let a=t?en[t]?.toLowerCase():null;return(a&&e?.supported_modes_by_provider?e.supported_modes_by_provider[a]:void 0)??e?.supported_modes},em=e=>!!e&&"Presidio PII"===es()[e],eu=e=>!!e&&"LiteLLM Content Filter"===es()[e],ep=e=>!!e&&"llm_as_a_judge"===en[e],eg="/ui/assets/logos/",ex={"Zscaler AI Guard":`${eg}zscaler.svg`,"Presidio PII":`${eg}microsoft_azure.svg`,"Bedrock Guardrail":`${eg}bedrock.svg`,Lakera:`${eg}lakeraai.jpeg`,"Azure Content Safety Prompt Shield":`${eg}microsoft_azure.svg`,"Azure Content Safety Text Moderation":`${eg}microsoft_azure.svg`,"Aporia AI":`${eg}aporia.png`,"PANW Prisma AIRS":`${eg}palo_alto_networks.jpeg`,"Cisco AI Defense":`${eg}cisco.png`,"Noma Security":`${eg}noma_security.png`,"Javelin Guardrails":`${eg}javelin.png`,"Pillar Guardrail":`${eg}pillar.jpeg`,"Google Cloud Model Armor":`${eg}google.svg`,"Guardrails AI":`${eg}guardrails_ai.jpeg`,"Lasso Guardrail":`${eg}lasso.png`,"Pangea Guardrail":`${eg}pangea.png`,"AIM Guardrail":`${eg}aim_security.jpeg`,"Cato Networks Guardrail":`${eg}cato_networks.svg`,"OpenAI Moderation":`${eg}openai_small.svg`,EnkryptAI:`${eg}enkrypt_ai.avif`,"Prompt Security":`${eg}prompt_security.png`,PromptGuard:`${eg}promptguard.svg`,XecGuard:`${eg}xecguard.svg`,"LiteLLM Content Filter":`${eg}litellm_logo.jpg`,"LiteLLM LLM as a Judge":`${eg}litellm_logo.jpg`,Akto:`${eg}akto.svg`,"Qostodian Nexus":`${eg}qohash.jpg`,"RepelloAI Argus":`${eg}repelloai.png`},eh=e=>{if(!e)return{logo:"",displayName:"-"};let t=Object.keys(en).find(t=>en[t].toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let a=es()[t];return{logo:(0,ea.resolveLogoSrc)(ex[a])??"",displayName:a||e}};function ef(e){return!0===e?"yes":!1===e?"no":"inherit"}function ey(e){return!0===e?"yes":!1===e?"no":"inherit"}var ej=e.i(435451);let{Title:e_}=f.Typography,eb=({field:e,fieldKey:t,fullFieldKey:a,value:s})=>{let[n,o]=r.default.useState([]),[d,c]=r.default.useState(e.dict_key_options||[]);return r.default.useEffect(()=>{if(s&&"object"==typeof s){let t=Object.keys(s);o(t.map(e=>({key:e,id:`${e}_${Date.now()}_${Math.random()}`}))),c((e.dict_key_options||[]).filter(e=>!t.includes(e)))}},[s,e.dict_key_options]),(0,l.jsxs)("div",{className:"space-y-3",children:[n.map(t=>(0,l.jsxs)("div",{className:"flex items-center space-x-3 p-3 border rounded-lg",children:[(0,l.jsx)("div",{className:"w-24 font-medium text-sm",children:t.key}),(0,l.jsx)("div",{className:"flex-1",children:(0,l.jsx)(u.Form.Item,{name:Array.isArray(a)?[...a,t.key]:[a,t.key],style:{marginBottom:0},initialValue:s&&"object"==typeof s?s[t.key]:void 0,normalize:"number"===e.dict_value_type?e=>{if(null==e||""===e)return;let t=Number(e);return isNaN(t)?e:t}:void 0,children:"number"===e.dict_value_type?(0,l.jsx)(ej.default,{step:1,width:200,placeholder:`Enter ${t.key} value`}):"boolean"===e.dict_value_type?(0,l.jsxs)(x.Select,{placeholder:`Select ${t.key} value`,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):(0,l.jsx)(p.Input,{placeholder:`Enter ${t.key} value`})})}),(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",onClick:()=>{var e,a;return e=t.id,a=t.key,void(o(n.filter(t=>t.id!==e)),c([...d,a].sort()))},children:"Remove"})]},t.id)),d.length>0&&(0,l.jsxs)("div",{className:"flex items-center space-x-3 mt-2",children:[(0,l.jsx)(x.Select,{placeholder:"Select category to configure",style:{width:200},onSelect:e=>e&&void(!e||(o([...n,{key:e,id:`${e}_${Date.now()}`}]),c(d.filter(t=>t!==e)))),value:void 0,children:d.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}),(0,l.jsx)("span",{className:"text-sm text-gray-500",children:"Select a category to add threshold configuration"})]})]})},ev=({optionalParams:e,parentFieldKey:t,values:a})=>e.fields&&0!==Object.keys(e.fields).length?(0,l.jsxs)("div",{className:"guardrail-optional-params",children:[(0,l.jsxs)("div",{className:"mb-8 pb-4 border-b border-gray-100",children:[(0,l.jsx)(e_,{level:3,className:"mb-2 font-semibold text-gray-900",children:"Optional Parameters"}),(0,l.jsx)("p",{className:"text-gray-600 text-sm",children:e.description||"Configure additional settings for this guardrail provider"})]}),(0,l.jsx)("div",{className:"space-y-8",children:Object.entries(e.fields).map(([e,r])=>{let i,s;return i=`${t}.${e}`,s=a?.[e],"dict"===r.type&&r.dict_key_options?(0,l.jsxs)("div",{className:"mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200",children:[(0,l.jsx)("div",{className:"mb-4 font-medium text-gray-900 text-base",children:e}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mb-4",children:r.description}),(0,l.jsx)(eb,{field:r,fieldKey:e,fullFieldKey:[t,e],value:s})]},i):(0,l.jsx)("div",{className:"mb-8 p-6 bg-white rounded-lg border border-gray-200 shadow-xs",children:(0,l.jsx)(u.Form.Item,{name:[t,e],label:(0,l.jsxs)("div",{className:"mb-2",children:[(0,l.jsx)("div",{className:"font-medium text-gray-900 text-base",children:e}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mt-1",children:r.description})]}),rules:r.required?[{required:!0,message:`${e} is required`}]:void 0,className:"mb-0",initialValue:void 0!==s?s:r.default_value,normalize:"number"===r.type?e=>{if(null==e||""===e)return;let t=Number(e);return isNaN(t)?e:t}:void 0,children:"select"===r.type&&r.options?(0,l.jsx)(x.Select,{placeholder:r.description,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"multiselect"===r.type&&r.options?(0,l.jsx)(x.Select,{mode:"multiple",placeholder:r.description,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"bool"===r.type||"boolean"===r.type?(0,l.jsxs)(x.Select,{placeholder:r.description,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):"number"===r.type?(0,l.jsx)(ej.default,{step:1,width:400,placeholder:r.description}):e.includes("password")||e.includes("secret")||e.includes("key")?(0,l.jsx)(p.Input.Password,{placeholder:r.description}):(0,l.jsx)(p.Input,{placeholder:r.description})})},i)})})]}):null;var ew=e.i(482725),eC=e.i(850627);let eN=({selectedProvider:e,accessToken:t,providerParams:a=null,value:i=null})=>{let[s,n]=(0,r.useState)(!1),[o,d]=(0,r.useState)(a),[c,g]=(0,r.useState)(null);if((0,r.useEffect)(()=>{if(a)return void d(a);let e=async()=>{if(t){n(!0),g(null);try{let e=await (0,m.getGuardrailProviderSpecificParams)(t);d(e),ei(e),eo(e)}catch(e){console.error("Error fetching provider params:",e),g("Failed to load provider parameters")}finally{n(!1)}}};a||e()},[t,a]),!e)return null;if(s)return(0,l.jsx)(ew.Spin,{tip:"Loading provider parameters..."});if(c)return(0,l.jsx)("div",{className:"text-red-500",children:c});let h=en[e]?.toLowerCase(),f=o&&o[h];if(!f||0===Object.keys(f).length)return(0,l.jsx)("div",{children:"No configuration fields available for this provider."});let y=new Set(["patterns","blocked_words","blocked_words_file","categories","severity_threshold","pattern_redaction_format","keyword_redaction_tag"]),j=eu(e),_=(e,t="",a)=>Object.entries(e).map(([e,r])=>{let s=t?`${t}.${e}`:e,n=a?a[e]:i?.[e];if("ui_friendly_name"===e||"optional_params"===e&&"nested"===r.type&&r.fields||j&&y.has(e))return null;if("nested"===r.type&&r.fields)return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"mb-2 font-medium",children:e}),(0,l.jsx)("div",{className:"ml-4 border-l-2 border-gray-200 pl-4",children:_(r.fields,s,n)})]},s);let o=void 0!==n?n:r.default_value??("percentage"===r.type?.5:void 0);return(0,l.jsx)(u.Form.Item,{name:s,label:e,tooltip:r.description,rules:r.required?[{required:!0,message:`${e} is required`}]:void 0,initialValue:o,children:"select"===r.type&&r.options?(0,l.jsx)(x.Select,{placeholder:r.description,defaultValue:n||r.default_value,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"multiselect"===r.type&&r.options?(0,l.jsx)(x.Select,{mode:"multiple",placeholder:r.description,defaultValue:n||r.default_value,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"bool"===r.type||"boolean"===r.type?(0,l.jsxs)(x.Select,{placeholder:r.description,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):"percentage"===r.type&&null!=r.min&&null!=r.max?(0,l.jsx)(eC.Slider,{min:r.min,max:r.max,step:r.step??.1,marks:{[r.min]:"0%",[(r.min+r.max)/2]:"50%",[r.max]:"100%"}}):"number"===r.type?(0,l.jsx)(ej.default,{step:1,width:400,placeholder:r.description,defaultValue:void 0!==n?Number(n):void 0}):e.includes("password")||e.includes("secret")||e.includes("key")?(0,l.jsx)(p.Input.Password,{placeholder:r.description,defaultValue:n||""}):(0,l.jsx)(p.Input,{placeholder:r.description,defaultValue:n||""})},s)});return(0,l.jsx)(l.Fragment,{children:_(f)})};var ek=e.i(592968),eS=e.i(750113);let eI=({availableModels:e,form:t})=>(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{style:{background:"#f6ffed",border:"1px solid #b7eb8f",borderRadius:6,padding:"10px 14px",marginBottom:16,fontSize:13,color:"#389e0d"},children:["After each LLM response, the ",(0,l.jsx)("strong",{children:"Judge Model"})," scores it 0–100 against your criteria. If the weighted average falls below the threshold, the response is blocked (or logged)."]}),(0,l.jsx)(u.Form.Item,{name:"judge_model",label:(0,l.jsxs)("span",{children:["Judge Model ",(0,l.jsx)(ek.Tooltip,{title:"The LLM that reads each response and grades it. Pick a capable model — it never sees end-user data beyond what the LLM returned.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),rules:[{required:!0,message:"Select a judge model"}],children:(0,l.jsx)(x.Select,{showSearch:!0,placeholder:"Select a model",options:e.map(e=>({label:e,value:e}))})}),(0,l.jsx)(u.Form.Item,{name:"overall_threshold",label:(0,l.jsxs)("span",{children:["Minimum Score to Pass ",(0,l.jsx)(ek.Tooltip,{title:"0–100. If the weighted average of criterion scores falls below this, the guardrail triggers. 80 is a good default.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),initialValue:80,children:(0,l.jsx)(J.InputNumber,{min:0,max:100,addonAfter:"/ 100",style:{width:"100%"}})}),(0,l.jsx)(u.Form.Item,{name:"on_failure",label:(0,l.jsxs)("span",{children:["On Failure ",(0,l.jsx)(ek.Tooltip,{title:"Block: return HTTP 422 when the score is too low. Log: record the result but let the response through.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),initialValue:"block",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"block",children:"Block (return 422)"}),(0,l.jsx)(x.Select.Option,{value:"log",children:"Log only"})]})}),(0,l.jsx)(u.Form.Item,{label:(0,l.jsxs)("span",{children:["Evaluation Criteria ",(0,l.jsx)(ek.Tooltip,{title:"Each criterion is something the judge checks. Weights must add up to 100%.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),children:(0,l.jsx)(u.Form.List,{name:"criteria",initialValue:[{name:"",weight:100,description:""}],children:(e,{add:a,remove:r})=>(0,l.jsxs)(l.Fragment,{children:[e.map(({key:e,name:t,...a})=>(0,l.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,padding:"12px 12px 0",marginBottom:8},children:[(0,l.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"flex-end"},children:[(0,l.jsx)(u.Form.Item,{...a,name:[t,"name"],rules:[{required:!0,message:"Enter criterion name"}],style:{flex:2,marginBottom:8},children:(0,l.jsx)(p.Input,{placeholder:"Criterion name (e.g. Policy accuracy)"})}),(0,l.jsx)(u.Form.Item,{...a,name:[t,"weight"],label:(0,l.jsx)(ek.Tooltip,{title:"How much this criterion counts toward the final score. All weights must add up to 100%.",children:(0,l.jsxs)("span",{style:{fontSize:12,color:"#595959"},children:["Weight ",(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#bfbfbf"}})]})}),rules:[{required:!0,message:"Enter weight"}],style:{flex:1,marginBottom:8},children:(0,l.jsx)(J.InputNumber,{min:0,max:100,addonAfter:"%",style:{width:"100%"},placeholder:"e.g. 50"})}),(0,l.jsx)("div",{style:{marginBottom:8},children:(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",onClick:()=>r(t),children:"×"})})]}),(0,l.jsx)(u.Form.Item,{...a,name:[t,"description"],rules:[{required:!0,message:"Describe what to check"}],style:{marginBottom:8},children:(0,l.jsx)(p.Input,{placeholder:"What should the judge check for this criterion?"})})]},e)),(0,l.jsx)(i.Button,{type:"dashed",block:!0,style:{marginTop:4},onClick:()=>a({name:"",weight:0,description:""}),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add Criterion"}),e.length>0&&(0,l.jsx)(u.Form.Item,{shouldUpdate:!0,noStyle:!0,children:()=>{let e=(t.getFieldValue("criteria")||[]).reduce((e,t)=>e+(Number(t?.weight)||0),0),a=100===e;return(0,l.jsxs)("div",{style:{marginTop:6,fontSize:12,color:a?"#52c41a":"#faad14"},children:["Weights total: ",e,"%",a?" ✓":" — must add up to 100%"]})}})]})})})]});var eA=e.i(536916),eO=e.i(149192),eT=e.i(741585),eT=eT,eP=e.i(724154);e.i(247167);var eL=e.i(931067);let eB={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880.1 154H143.9c-24.5 0-39.8 26.7-27.5 48L349 597.4V838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V597.4L907.7 202c12.2-21.3-3.1-48-27.6-48zM603.4 798H420.6V642h182.9v156zm9.6-236.6l-9.5 16.6h-183l-9.5-16.6L212.7 226h598.6L613 561.4z"}}]},name:"filter",theme:"outlined"};var eF=e.i(9583),e$=r.forwardRef(function(e,t){return r.createElement(eF.default,(0,eL.default)({},e,{ref:t,icon:eB}))});let{Text:eE}=f.Typography,{Option:eM}=x.Select,eR=({categories:e,selectedCategories:t,onChange:a})=>(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center mb-2",children:[(0,l.jsx)(e$,{className:"text-gray-500 mr-1"}),(0,l.jsx)(eE,{className:"text-gray-500 font-medium",children:"Filter by category"})]}),(0,l.jsx)(x.Select,{mode:"multiple",placeholder:"Select categories to filter by",style:{width:"100%"},onChange:a,value:t,allowClear:!0,showSearch:!0,optionFilterProp:"children",className:"mb-4",tagRender:e=>(0,l.jsx)(h.Tag,{color:"blue",closable:e.closable,onClose:e.onClose,className:"mr-2 mb-2",children:e.label}),children:e.map(e=>(0,l.jsx)(eM,{value:e.category,children:e.category},e.category))})]}),eG=({onSelectAll:e,onUnselectAll:t,hasSelectedEntities:a})=>(0,l.jsxs)("div",{className:"bg-gray-50 p-5 rounded-lg mb-6 border border-gray-200 shadow-xs",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-4",children:[(0,l.jsxs)("div",{className:"flex items-center",children:[(0,l.jsx)(eE,{strong:!0,className:"text-gray-700 text-base",children:"Quick Actions"}),(0,l.jsx)(ek.Tooltip,{title:"Apply action to all PII types at once",children:(0,l.jsx)("div",{className:"ml-2 text-gray-400 cursor-help text-xs",children:"ⓘ"})})]}),(0,l.jsx)(i.Button,{color:"danger",variant:"outlined",onClick:t,disabled:!a,icon:(0,l.jsx)(eO.CloseOutlined,{}),children:"Unselect All"})]}),(0,l.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,l.jsx)(i.Button,{color:"primary",variant:"outlined",onClick:()=>e("MASK"),className:"h-10",block:!0,icon:(0,l.jsx)(eT.default,{}),children:"Select All & Mask"}),(0,l.jsx)(i.Button,{color:"danger",variant:"outlined",onClick:()=>e("BLOCK"),className:"h-10 hover:bg-red-100",block:!0,icon:(0,l.jsx)(eP.StopOutlined,{}),children:"Select All & Block"})]})]}),ez=({entities:e,selectedEntities:t,selectedActions:a,actions:r,onEntitySelect:i,onActionSelect:s,entityToCategoryMap:n})=>(0,l.jsxs)("div",{className:"border rounded-lg overflow-hidden shadow-xs",children:[(0,l.jsxs)("div",{className:"bg-gray-50 px-5 py-3 border-b flex",children:[(0,l.jsx)(eE,{strong:!0,className:"flex-1 text-gray-700",children:"PII Type"}),(0,l.jsx)(eE,{strong:!0,className:"w-32 text-right text-gray-700",children:"Action"})]}),(0,l.jsx)("div",{className:"max-h-[400px] overflow-y-auto",children:0===e.length?(0,l.jsx)("div",{className:"py-10 text-center text-gray-500",children:"No PII types match your filter criteria"}):e.map(e=>(0,l.jsxs)("div",{className:`px-5 py-3 flex items-center justify-between hover:bg-gray-50 border-b ${t.includes(e)?"bg-blue-50":""}`,children:[(0,l.jsxs)("div",{className:"flex items-center flex-1",children:[(0,l.jsx)(eA.Checkbox,{checked:t.includes(e),onChange:()=>i(e),className:"mr-3"}),(0,l.jsx)(eE,{className:t.includes(e)?"font-medium text-gray-900":"text-gray-700",children:e.replace(/_/g," ")}),n.get(e)&&(0,l.jsx)(h.Tag,{className:"ml-2 text-xs",color:"blue",children:n.get(e)})]}),(0,l.jsx)("div",{className:"w-32",children:(0,l.jsx)(x.Select,{value:t.includes(e)&&a[e]||"MASK",onChange:t=>s(e,t),style:{width:120},disabled:!t.includes(e),className:`${!t.includes(e)?"opacity-50":""}`,dropdownMatchSelectWidth:!1,children:r.map(e=>(0,l.jsx)(eM,{value:e,children:(0,l.jsxs)("div",{className:"flex items-center",children:[(e=>{switch(e){case"MASK":return(0,l.jsx)(eT.default,{style:{marginRight:4}});case"BLOCK":return(0,l.jsx)(eP.StopOutlined,{style:{marginRight:4}});default:return null}})(e),e]})},e))})})]},e))})]}),{Title:eD,Text:eK}=f.Typography,eq=({entities:e,actions:t,selectedEntities:a,selectedActions:i,onEntitySelect:s,onActionSelect:n,entityCategories:o=[]})=>{let[d,c]=(0,r.useState)([]),m=new Map;o.forEach(e=>{e.entities.forEach(t=>{m.set(t,e.category)})});let u=e.filter(e=>0===d.length||d.includes(m.get(e)||""));return(0,l.jsxs)("div",{className:"pii-configuration",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-5",children:[(0,l.jsx)("div",{className:"flex items-center",children:(0,l.jsx)(eD,{level:4,className:"m-0! font-semibold text-gray-800",children:"Configure PII Protection"})}),(0,l.jsxs)(eK,{className:"text-gray-500",children:[a.length," items selected"]})]}),(0,l.jsxs)("div",{className:"mb-6",children:[(0,l.jsx)(eR,{categories:o,selectedCategories:d,onChange:c}),(0,l.jsx)(eG,{onSelectAll:t=>{e.forEach(e=>{a.includes(e)||s(e),n(e,t)})},onUnselectAll:()=>{a.forEach(e=>{s(e)})},hasSelectedEntities:a.length>0})]}),(0,l.jsx)(ez,{entities:u,selectedEntities:a,selectedActions:i,actions:t,onEntitySelect:s,onActionSelect:n,entityToCategoryMap:m})]})};var eH=e.i(304967),eU=e.i(599724),eJ=e.i(312361),eW=e.i(21548),eV=e.i(827252);let eY={rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""},eQ=({value:e,onChange:t,disabled:a=!1})=>{let r={...eY,...e||{},rules:e?.rules?[...e.rules]:[]},s=e=>{let a={...r,...e};t?.(a)},n=(e,t)=>{s({rules:r.rules.map((a,l)=>l===e?{...a,...t}:a)})},o=(e,t)=>{let a=r.rules[e];if(!a)return;let l=Object.entries(a.allowed_param_patterns||{});t(l);let i={};l.forEach(([e,t])=>{i[e]=t}),n(e,{allowed_param_patterns:Object.keys(i).length>0?i:void 0})};return(0,l.jsxs)(eH.Card,{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"LiteLLM Tool Permission Guardrail"}),(0,l.jsx)(eU.Text,{className:"text-sm text-gray-500",children:"Provide regex patterns (e.g., ^mcp__github_.*$) for tool names or types and optionally constrain payload fields."})]}),!a&&(0,l.jsx)(i.Button,{icon:(0,l.jsx)(d.PlusOutlined,{}),type:"primary",onClick:()=>{s({rules:[...r.rules,{id:`rule_${Math.random().toString(36).slice(2,8)}`,decision:"allow",allowed_param_patterns:void 0}]})},className:"bg-blue-600! text-white! hover:bg-blue-500!",children:"Add Rule"})]}),(0,l.jsx)(eJ.Divider,{}),0===r.rules.length?(0,l.jsx)(eW.Empty,{description:"No tool rules added yet"}):(0,l.jsx)("div",{className:"space-y-4",children:r.rules.map((e,t)=>{let d;return(0,l.jsxs)(eH.Card,{className:"bg-gray-50",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,l.jsxs)(eU.Text,{className:"font-semibold",children:["Rule ",t+1]}),(0,l.jsx)(i.Button,{icon:(0,l.jsx)(L.DeleteOutlined,{}),danger:!0,type:"text",disabled:a,onClick:()=>{s({rules:r.rules.filter((e,a)=>a!==t)})},children:"Remove"})]}),(0,l.jsxs)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Rule ID"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"unique_rule_id",value:e.id,onChange:e=>n(t,{id:e.target.value})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Tool Name (optional)"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^mcp__github_.*$",value:e.tool_name??"",onChange:e=>n(t,{tool_name:""===e.target.value.trim()?void 0:e.target.value})})]})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 mt-4",children:(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Tool Type (optional)"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^function$",value:e.tool_type??"",onChange:e=>n(t,{tool_type:""===e.target.value.trim()?void 0:e.target.value})})]})}),(0,l.jsxs)("div",{className:"mt-4 flex flex-col gap-2",children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Decision"}),(0,l.jsxs)(x.Select,{disabled:a,value:e.decision,style:{width:200},onChange:e=>n(t,{decision:e}),children:[(0,l.jsx)(x.Select.Option,{value:"allow",children:"Allow"}),(0,l.jsx)(x.Select.Option,{value:"deny",children:"Deny"})]})]}),(0,l.jsx)("div",{className:"mt-4",children:0===(d=Object.entries(e.allowed_param_patterns||{})).length?(0,l.jsx)(i.Button,{disabled:a,size:"small",onClick:()=>n(t,{allowed_param_patterns:{"":""}}),children:"+ Restrict tool arguments (optional)"}):(0,l.jsxs)("div",{className:"space-y-2",children:[(0,l.jsx)(eU.Text,{className:"text-sm text-gray-600",children:"Argument constraints (dot or array paths)"}),d.map(([r,s],n)=>(0,l.jsxs)(j.Space,{align:"start",children:[(0,l.jsx)(p.Input,{disabled:a,placeholder:"messages[0].content",value:r,onChange:e=>{var a;return a=e.target.value,void o(t,e=>{if(!e[n])return;let[,t]=e[n];e[n]=[a,t]})}}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^email@.*$",value:s,onChange:e=>{var a;return a=e.target.value,void o(t,e=>{if(!e[n])return;let[t]=e[n];e[n]=[t,a]})}}),(0,l.jsx)(i.Button,{disabled:a,icon:(0,l.jsx)(L.DeleteOutlined,{}),danger:!0,onClick:()=>o(t,e=>{e.splice(n,1)})})]},`${e.id||t}-${n}`)),(0,l.jsx)(i.Button,{disabled:a,size:"small",onClick:()=>n(t,{allowed_param_patterns:{...e.allowed_param_patterns||{},"":""}}),children:"+ Add another constraint"})]})})]},e.id||t)})}),(0,l.jsx)(eJ.Divider,{}),(0,l.jsxs)("div",{className:"grid gap-4 md:grid-cols-2",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Default action"}),(0,l.jsxs)(x.Select,{disabled:a,value:r.default_action,onChange:e=>s({default_action:e}),children:[(0,l.jsx)(x.Select.Option,{value:"allow",children:"Allow"}),(0,l.jsx)(x.Select.Option,{value:"deny",children:"Deny"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)(eU.Text,{className:"text-sm font-medium flex items-center gap-1",children:["On disallowed action",(0,l.jsx)(ek.Tooltip,{title:"Block returns an error when a forbidden tool is invoked. Rewrite strips the tool call but lets the rest of the response continue.",children:(0,l.jsx)(eV.InfoCircleOutlined,{})})]}),(0,l.jsxs)(x.Select,{disabled:a,value:r.on_disallowed_action,onChange:e=>s({on_disallowed_action:e}),children:[(0,l.jsx)(x.Select.Option,{value:"block",children:"Block"}),(0,l.jsx)(x.Select.Option,{value:"rewrite",children:"Rewrite"})]})]})]}),(0,l.jsxs)("div",{className:"mt-4",children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Violation message (optional)"}),(0,l.jsx)(p.Input.TextArea,{disabled:a,rows:3,placeholder:"This violates our org policy...",value:r.violation_message_template,onChange:e=>s({violation_message_template:e.target.value})})]})]})},{Title:eX,Text:eZ,Link:e0}=f.Typography,{Option:e1}=x.Select,e2={pre_call:"Before LLM Call - Runs before the LLM call and checks the input (Recommended)",during_call:"During LLM Call - Runs in parallel with the LLM call, with response held until check completes",post_call:"After LLM Call - Runs after the LLM call and checks only the output",logging_only:"Logging Only - Only runs on logging callbacks without affecting the LLM call",pre_mcp_call:"Before MCP Tool Call - Runs before MCP tool execution and validates tool calls",during_mcp_call:"During MCP Tool Call - Runs in parallel with MCP tool execution for monitoring"},e4=()=>({rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""}),e5=({visible:e,onClose:t,accessToken:a,onSuccess:s,preset:n})=>{let[o]=u.Form.useForm(),[d,c]=(0,r.useState)(!1),[f,j]=(0,r.useState)(null),[_,b]=(0,r.useState)(null),[v,w]=(0,r.useState)([]),[C,N]=(0,r.useState)({}),[k,S]=(0,r.useState)(0),[I,A]=(0,r.useState)(null),[O,T]=(0,r.useState)([]),[P,L]=(0,r.useState)(2),[B,F]=(0,r.useState)({}),[$,E]=(0,r.useState)([]),[M,R]=(0,r.useState)([]),[G,z]=(0,r.useState)([]),[D,K]=(0,r.useState)(""),[q,H]=(0,r.useState)(!1),[U,J]=(0,r.useState)(null),[W,V]=(0,r.useState)(""),[Y,Q]=(0,r.useState)(void 0),[X,Z]=(0,r.useState)("warn"),[ee,el]=(0,r.useState)(""),[er,eg]=(0,r.useState)(!1),[eh,ef]=(0,r.useState)([]),[ey,ej]=(0,r.useState)(e4),e_=(0,r.useMemo)(()=>!!f&&"tool_permission"===(en[f]||"").toLowerCase(),[f]);(0,r.useEffect)(()=>{a&&(async()=>{try{let[e,t,l]=await Promise.all([(0,m.getGuardrailUISettings)(a),(0,m.getGuardrailProviderSpecificParams)(a),(0,m.modelAvailableCall)(a,"","").catch(()=>null)]);b(e),A(t),l?.data&&ef(l.data.map(e=>e.id)),ei(t),eo(t)}catch(e){console.error("Error fetching guardrail data:",e),y.default.fromBackend("Failed to load guardrail configuration")}})()},[a]),(0,r.useEffect)(()=>{if(!n||!e||!_)return;j(n.provider);let t={provider:n.provider,guardrail_name:n.guardrailNameSuggestion,mode:n.mode,default_on:n.defaultOn,skip_system_message_choice:"inherit",skip_tool_message_choice:"inherit"};if("BlockCodeExecution"===n.provider&&(t.confidence_threshold=.5),o.setFieldsValue(t),n.categoryName&&_.content_filter_settings?.content_categories){let e=_.content_filter_settings.content_categories.find(e=>e.name===n.categoryName);e&&z([{id:`category-${Date.now()}`,category:e.name,display_name:e.display_name,action:e.default_action,severity_threshold:"medium"}])}},[n,e,_,o]);let eb=e=>{j(e);let t={config:void 0,presidio_analyzer_api_base:void 0,presidio_anonymizer_api_base:void 0};"BlockCodeExecution"===e&&(t.confidence_threshold=.5);let a=en[e]?.toLowerCase(),l=a&&_?.supported_modes_by_provider?_.supported_modes_by_provider[a]:void 0;if(l){let e=ed(o.getFieldValue("mode")),a=e.filter(e=>l.includes(e));a.length!==e.length&&(t.mode=a.length>0?a:void 0)}o.setFieldsValue(t),w([]),N({}),T([]),L(2),F({}),E([]),R([]),z([]),K(""),H(!1),J(null),ej(e4()),"LlmAsAJudge"===e&&o.setFieldsValue({mode:"post_call"})},ew=e=>{w(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},eC=(e,t)=>{N(a=>({...a,[e]:t}))},ek=async()=>{try{if(0===k&&(await o.validateFields(["guardrail_name","provider","mode","default_on"]),f)){let e=["guardrail_name","provider","mode","default_on"];"PresidioPII"===f&&e.push("presidio_analyzer_api_base","presidio_anonymizer_api_base"),await o.validateFields(e)}if(1===k&&em(f)&&0===v.length)return void y.default.fromBackend("Please select at least one PII entity to continue");S(k+1)}catch(e){console.error("Form validation failed:",e)}},eS=()=>{o.resetFields(),j(null),w([]),N({}),T([]),L(2),F({}),E([]),R([]),z([]),K(""),ej(e4()),V(""),Q(void 0),Z("warn"),el(""),eg(!1),S(0)},eA=()=>{eS(),t()},eO=async()=>{try{var e,l;c(!0),await o.validateFields();let r=o.getFieldsValue(!0),i=en[r.provider],n={guardrail_name:r.guardrail_name,litellm_params:{guardrail:i,mode:r.mode,default_on:r.default_on},guardrail_info:{}},d=(e=r.skip_system_message_choice,"yes"===e||"no"!==e&&void 0);void 0!==d&&(n.litellm_params.skip_system_message_in_guardrail=d);let u=(l=r.skip_tool_message_choice,"yes"===l||"no"!==l&&void 0);if(void 0!==u&&(n.litellm_params.skip_tool_message_in_guardrail=u),"PresidioPII"===r.provider&&v.length>0){let e={};v.forEach(t=>{e[t]=C[t]||"MASK"}),n.litellm_params.pii_entities_config=e,r.presidio_analyzer_api_base&&(n.litellm_params.presidio_analyzer_api_base=r.presidio_analyzer_api_base),r.presidio_anonymizer_api_base&&(n.litellm_params.presidio_anonymizer_api_base=r.presidio_anonymizer_api_base)}if(eu(r.provider)){let e=q&&(U?.brand_self?.length??0)>0;if(!($.length>0||M.length>0||G.length>0)&&!e){y.default.fromBackend("Please configure at least one content filter setting (category, pattern, keyword, or competitor intent)"),c(!1);return}$.length>0&&(n.litellm_params.patterns=$.map(e=>({pattern_type:"prebuilt"===e.type?"prebuilt":"regex",pattern_name:"prebuilt"===e.type?e.name:void 0,pattern:"custom"===e.type?e.pattern:void 0,name:e.name,action:e.action}))),M.length>0&&(n.litellm_params.blocked_words=M.map(e=>({keyword:e.keyword,action:e.action,description:e.description}))),G.length>0&&(n.litellm_params.categories=G.map(e=>({category:e.category,enabled:!0,action:e.action,severity_threshold:e.severity_threshold||"medium"}))),e&&U&&(n.litellm_params.competitor_intent_config={competitor_intent_type:U.competitor_intent_type??"airline",brand_self:U.brand_self,locations:(U.locations?.length??0)>0?U.locations:void 0,competitors:"generic"===U.competitor_intent_type&&(U.competitors?.length??0)>0?U.competitors:void 0,policy:U.policy,threshold_high:U.threshold_high,threshold_medium:U.threshold_medium,threshold_low:U.threshold_low})}else if(r.config)try{n.guardrail_info=JSON.parse(r.config)}catch(e){y.default.fromBackend("Invalid JSON in configuration"),c(!1);return}if("llm_as_a_judge"===i){let e=r.criteria||[];if(0===e.length){y.default.fromBackend("Add at least one evaluation criterion"),c(!1);return}let t=e.reduce((e,t)=>e+(Number(t?.weight)||0),0);if(100!==t){y.default.fromBackend(`Criterion weights must sum to 100% (currently ${t}%)`),c(!1);return}n.litellm_params.judge_model=r.judge_model,n.litellm_params.overall_threshold=r.overall_threshold??80,n.litellm_params.on_failure=r.on_failure??"block",n.litellm_params.criteria=e.map(e=>({name:e.name,weight:Number(e.weight),description:e.description||""}))}if("tool_permission"===i){if(0===ey.rules.length){y.default.fromBackend("Add at least one tool permission rule"),c(!1);return}n.litellm_params.rules=ey.rules,n.litellm_params.default_action=ey.default_action,n.litellm_params.on_disallowed_action=ey.on_disallowed_action,ey.violation_message_template&&(n.litellm_params.violation_message_template=ey.violation_message_template)}if(eu(r.provider)&&(void 0!==Y&&Y>0&&(n.litellm_params.end_session_after_n_fails=Y),X&&"realtime"===W&&(n.litellm_params.on_violation=X),ee.trim()&&(n.litellm_params.realtime_violation_message=ee.trim())),I&&f&&"llm_as_a_judge"!==i){let e=I[en[f]?.toLowerCase()]||{},t=new Set;Object.keys(e).forEach(e=>{"optional_params"!==e&&t.add(e)}),e.optional_params&&e.optional_params.fields&&Object.keys(e.optional_params.fields).forEach(e=>{t.add(e)}),t.forEach(e=>{let t=r[e];(null==t||""===t)&&(t=r.optional_params?.[e]),null!=t&&""!==t&&(n.litellm_params[e]=t)})}if(!a)throw Error("No access token available");await (0,m.createGuardrailCall)(a,n),y.default.success("Guardrail created successfully"),eS(),s(),t()}catch(e){console.error("Failed to create guardrail:",e),y.default.fromBackend("Failed to create guardrail: "+(e instanceof Error?e.message:String(e)))}finally{c(!1)}},eT=e=>{if(!_||!eu(f))return null;let t=_.content_filter_settings;return t?(0,l.jsx)(et,{prebuiltPatterns:t.prebuilt_patterns||[],categories:t.pattern_categories||[],selectedPatterns:$,blockedWords:M,onPatternAdd:e=>E([...$,e]),onPatternRemove:e=>E($.filter(t=>t.id!==e)),onPatternActionChange:(e,t)=>{E($.map(a=>a.id===e?{...a,action:t}:a))},onBlockedWordAdd:e=>R([...M,e]),onBlockedWordRemove:e=>R(M.filter(t=>t.id!==e)),onBlockedWordUpdate:(e,t,a)=>{R(M.map(l=>l.id===e?{...l,[t]:a}:l))},contentCategories:t.content_categories||[],selectedContentCategories:G,onContentCategoryAdd:e=>z([...G,e]),onContentCategoryRemove:e=>z(G.filter(t=>t.id!==e)),onContentCategoryUpdate:(e,t,a)=>{z(G.map(l=>l.id===e?{...l,[t]:a}:l))},pendingCategorySelection:D,onPendingCategorySelectionChange:K,accessToken:a,showStep:e,competitorIntentEnabled:q,competitorIntentConfig:U,onCompetitorIntentChange:(e,t)=>{H(e),J(t)}}):null},eP=eu(f)?[{title:"Basic Info",optional:!1},{title:"Topics",optional:!1},{title:"Patterns",optional:!1},{title:"Keywords",optional:!1},{title:"Endpoint Settings (Optional)",optional:!0}]:em(f)?[{title:"Basic Info",optional:!1},{title:"PII Configuration",optional:!1}]:[{title:"Basic Info",optional:!1},{title:"Provider Configuration",optional:!1}];return(0,l.jsx)(g.Modal,{title:null,open:e,onCancel:eA,maskClosable:!1,footer:null,width:1e3,closable:!1,className:"top-8",styles:{body:{padding:0}},children:(0,l.jsxs)("div",{className:"flex flex-col",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between px-6 py-4 border-b border-gray-200",children:[(0,l.jsx)("h3",{className:"text-base font-semibold text-gray-900 m-0",children:"Create guardrail"}),(0,l.jsx)("button",{onClick:eA,className:"text-gray-400 hover:text-gray-600 bg-transparent border-none cursor-pointer text-base leading-none p-1",children:"✕"})]}),(0,l.jsx)("div",{className:"overflow-auto px-6 py-4",style:{maxHeight:"calc(80vh - 120px)"},children:(0,l.jsx)(u.Form,{form:o,layout:"vertical",initialValues:{mode:"pre_call",default_on:!1,skip_system_message_choice:"inherit",skip_tool_message_choice:"inherit"},children:eP.map((e,t)=>{let r=t{r&&S(t)},style:{minHeight:24},children:[(0,l.jsx)("span",{className:"text-sm",style:{fontWeight:i?600:500,color:i?"#1e293b":r?"#4f46e5":"#94a3b8"},children:e.title}),e.optional&&!i&&(0,l.jsx)("span",{className:"text-[11px] text-slate-400",children:"optional"}),r&&(0,l.jsx)("span",{className:"text-[11px] text-indigo-500 hover:underline",children:"Edit"})]}),i&&(0,l.jsx)("div",{className:"mt-3",children:(()=>{switch(k){case 0:let e;return e=!e_&&!eu(f)&&!ep(f),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(u.Form.Item,{name:"guardrail_name",label:"Guardrail Name",rules:[{required:!0,message:"Please enter a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"Enter a name for this guardrail"})}),(0,l.jsx)(u.Form.Item,{name:"provider",label:"Guardrail Provider",rules:[{required:!0,message:"Please select a provider"}],children:(0,l.jsx)(x.Select,{placeholder:"Select a guardrail provider",onChange:eb,labelInValue:!1,optionLabelProp:"label",dropdownRender:e=>e,showSearch:!0,children:Object.entries(es()).map(([e,t])=>(0,l.jsx)(e1,{value:e,label:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ex[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ex[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]}),children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ex[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ex[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]})},e))})}),(0,l.jsx)(u.Form.Item,{name:"mode",label:"Mode",tooltip:"How the guardrail should be applied",rules:[{required:!0,message:"Please select a mode"}],children:(0,l.jsx)(x.Select,{optionLabelProp:"label",mode:"multiple",children:ec(_,f)?.map(e=>(0,l.jsx)(e1,{value:e,label:e,children:(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:e}),"pre_call"===e&&(0,l.jsx)(h.Tag,{color:"green",style:{marginLeft:"8px"},children:"Recommended"})]}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2[e]})]})},e))||(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(e1,{value:"pre_call",label:"pre_call",children:(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"pre_call"})," ",(0,l.jsx)(h.Tag,{color:"green",children:"Recommended"})]}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.pre_call})]})}),(0,l.jsx)(e1,{value:"during_call",label:"during_call",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"during_call"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.during_call})]})}),(0,l.jsx)(e1,{value:"post_call",label:"post_call",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"post_call"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.post_call})]})}),(0,l.jsx)(e1,{value:"logging_only",label:"logging_only",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"logging_only"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.logging_only})]})})]})})}),(0,l.jsx)(u.Form.Item,{name:"default_on",label:"Always On",tooltip:"If enabled, this guardrail will be applied to all requests by default.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"Yes"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"No"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_system_message_choice",label:"Skip system messages in guardrail",tooltip:"Unified guardrails only: omit role: system from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_tool_message_choice",label:"Skip tool messages in guardrail",tooltip:"Unified guardrails only: omit role: tool from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),e&&(0,l.jsx)(eN,{selectedProvider:f,accessToken:a,providerParams:I})]});case 1:if(em(f))return _&&"PresidioPII"===f?(0,l.jsx)(eq,{entities:_.supported_entities,actions:_.supported_actions,selectedEntities:v,selectedActions:C,onEntitySelect:ew,onActionSelect:eC,entityCategories:_.pii_entity_categories}):null;if(eu(f))return eT("categories");if(ep(f))return(0,l.jsx)(eI,{availableModels:eh,form:o});if(!f)return null;if(e_)return(0,l.jsx)(eQ,{value:ey,onChange:ej});if(!I)return null;let t=en[f]?.toLowerCase(),r=I&&I[t];return r&&r.optional_params?(0,l.jsx)(ev,{optionalParams:r.optional_params,parentFieldKey:"optional_params"}):null;case 2:if(eu(f))return eT("patterns");return null;case 3:if(eu(f))return eT("keywords");return null;case 4:return(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsx)("div",{children:(0,l.jsxs)("p",{className:"text-sm text-gray-500",children:["Configure settings for a specific call type. Most guardrails don't need this — skip it unless you're using a specific endpoint like ",(0,l.jsx)("code",{children:"/v1/realtime"}),"."]})}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Call type"}),(0,l.jsx)(x.Select,{placeholder:"Select a call type",value:W||void 0,onChange:e=>{V(e),eg(!1)},style:{width:260},allowClear:!0,options:[{value:"realtime",label:"/v1/realtime"}]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"More call types coming soon."})]}),"realtime"===W&&(0,l.jsxs)("div",{className:"border border-gray-200 rounded-lg overflow-hidden",children:[(0,l.jsxs)("button",{type:"button",onClick:()=>eg(e=>!e),className:"w-full flex items-center justify-between px-4 py-3 bg-gray-50 hover:bg-gray-100 text-sm font-medium text-gray-700",children:[(0,l.jsx)("span",{children:"/v1/realtime settings"}),(0,l.jsx)("svg",{className:`w-4 h-4 text-gray-500 transition-transform ${er?"rotate-180":""}`,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,l.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})})]}),er&&(0,l.jsxs)("div",{className:"space-y-5 px-4 py-4 border-t border-gray-200",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"End session after X violations"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Automatically close the session after this many guardrail violations. Leave empty to never auto-close."}),(0,l.jsx)("input",{type:"number",min:1,placeholder:"e.g. 3",value:Y??"",onChange:e=>Q(e.target.value?parseInt(e.target.value,10):void 0),className:"border border-gray-300 rounded-sm px-3 py-1.5 text-sm w-32"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"On violation"}),(0,l.jsx)("div",{className:"space-y-2",children:["warn","end_session"].map(e=>(0,l.jsxs)("label",{className:"flex items-start gap-2 cursor-pointer",children:[(0,l.jsx)("input",{type:"radio",name:"on_violation",value:e,checked:X===e,onChange:()=>Z(e),className:"mt-0.5"}),(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"text-sm font-medium text-gray-800",children:"warn"===e?"Warn":"End session"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 m-0",children:"warn"===e?"Bot speaks the message, session continues":"Bot speaks the message, connection closes immediately"})]})]},e))})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Message the user hears"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"What the bot says aloud when this guardrail fires. Falls back to the default violation message if empty."}),(0,l.jsx)("textarea",{rows:3,placeholder:"e.g. I'm not able to continue this conversation. Please contact us at 1-800-774-2678.",value:ee,onChange:e=>el(e.target.value),className:"border border-gray-300 rounded-sm px-3 py-2 text-sm w-full resize-none"})]})]})]})]});default:return null}})()})]})]},t)})})}),(0,l.jsxs)("div",{className:"flex items-center justify-end space-x-3 px-6 py-3 border-t border-gray-200",children:[(0,l.jsx)(i.Button,{onClick:eA,children:"Cancel"}),k>0&&(0,l.jsx)(i.Button,{onClick:()=>{S(k-1)},children:"Previous"}),k{let d,c,[h]=u.Form.useForm(),[f,j]=(0,r.useState)(!1),[_,b]=(0,r.useState)(o?.provider||null),[v,w]=(0,r.useState)(null),[C,N]=(0,r.useState)([]),[k,S]=(0,r.useState)({});(0,r.useEffect)(()=>{(async()=>{try{if(!a)return;let e=await (0,m.getGuardrailUISettings)(a);w(e)}catch(e){console.error("Error fetching guardrail settings:",e),y.default.fromBackend("Failed to load guardrail settings")}})()},[a]),(0,r.useEffect)(()=>{o?.pii_entities_config&&Object.keys(o.pii_entities_config).length>0&&(N(Object.keys(o.pii_entities_config)),S(o.pii_entities_config))},[o]);let I=e=>{N(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},A=(e,t)=>{S(a=>({...a,[e]:t}))},O=async()=>{try{j(!0);let e=await h.validateFields(),l=en[e.provider],r=n&&"object"==typeof n?{...n}:{};r.guardrail=l,r.mode=e.mode,r.default_on=e.default_on;let o=e.skip_system_message_choice;"yes"===o?r.skip_system_message_in_guardrail=!0:"no"===o?r.skip_system_message_in_guardrail=!1:delete r.skip_system_message_in_guardrail;let d=e.skip_tool_message_choice;"yes"===d?r.skip_tool_message_in_guardrail=!0:"no"===d?r.skip_tool_message_in_guardrail=!1:delete r.skip_tool_message_in_guardrail;let c={};if("PresidioPII"===e.provider&&C.length>0){let e={};C.forEach(t=>{e[t]=k[t]||"MASK"}),r.pii_entities_config=e}else if(e.config)try{let t=JSON.parse(e.config);"Bedrock"===e.provider&&t?(t.guardrail_id&&(r.guardrailIdentifier=t.guardrail_id),t.guardrail_version&&(r.guardrailVersion=t.guardrail_version)):c=t}catch(e){y.default.fromBackend("Invalid JSON in configuration"),j(!1);return}let u={guardrail_id:s,guardrail:{guardrail_name:e.guardrail_name,litellm_params:r,guardrail_info:c}};if(!a)throw Error("No access token available");let p=`/guardrails/${s}`,g=await fetch(p,{method:"PUT",headers:{[(0,m.getGlobalLitellmHeaderName)()]:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify(u)});if(!g.ok){let e=await g.text();throw Error(e||"Failed to update guardrail")}y.default.success("Guardrail updated successfully"),i(),t()}catch(e){console.error("Failed to update guardrail:",e),y.default.fromBackend("Failed to update guardrail: "+(e instanceof Error?e.message:String(e)))}finally{j(!1)}};return(0,l.jsx)(g.Modal,{title:"Edit Guardrail",open:e,onCancel:t,footer:null,width:700,children:(0,l.jsxs)(u.Form,{form:h,layout:"vertical",initialValues:o,children:[(0,l.jsx)(u.Form.Item,{name:"guardrail_name",label:"Guardrail Name",rules:[{required:!0,message:"Please enter a guardrail name"}],children:(0,l.jsx)(tu.TextInput,{placeholder:"Enter a name for this guardrail"})}),(0,l.jsx)(u.Form.Item,{name:"provider",label:"Guardrail Provider",rules:[{required:!0,message:"Please select a provider"}],children:(0,l.jsx)(x.Select,{placeholder:"Select a guardrail provider",onChange:e=>{b(e),h.setFieldsValue({config:void 0}),N([]),S({})},disabled:!0,optionLabelProp:"label",children:Object.entries(es()).map(([e,t])=>(0,l.jsx)(tx,{value:e,label:t,children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ex[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ex[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]})},e))})}),(0,l.jsx)(u.Form.Item,{name:"mode",label:"Mode",tooltip:"How the guardrail should be applied",rules:[{required:!0,message:"Please select a mode"}],children:(0,l.jsx)(x.Select,{children:(d=ec(v,_)??["pre_call","post_call"],[...c=ed(o?.mode).filter(e=>!d.includes(e)),...d].map(e=>(0,l.jsx)(tx,{value:e,children:c.includes(e)?`${e} (not supported by ${_}, pick another)`:e},e)))})}),(0,l.jsx)(u.Form.Item,{name:"default_on",label:"Always On",tooltip:"If enabled, this guardrail will be applied to all requests by default",valuePropName:"checked",children:(0,l.jsx)(U.Switch,{})}),(0,l.jsx)(u.Form.Item,{name:"skip_system_message_choice",label:"Skip system messages in guardrail",tooltip:"Unified guardrails only: whether role: system content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(tx,{value:"inherit",children:"Use global default"}),(0,l.jsx)(tx,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(tx,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_tool_message_choice",label:"Skip tool messages in guardrail",tooltip:"Unified guardrails only: whether role: tool content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(tx,{value:"inherit",children:"Use global default"}),(0,l.jsx)(tx,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(tx,{value:"no",children:"No — always include in scan"})]})}),(()=>{if(!_)return null;if("PresidioPII"===_)return v&&_&&"PresidioPII"===_?(0,l.jsx)(eq,{entities:v.supported_entities,actions:v.supported_actions,selectedEntities:C,selectedActions:k,onEntitySelect:I,onActionSelect:A,entityCategories:v.pii_entity_categories}):null;switch(_){case"Aporia":return(0,l.jsx)(u.Form.Item,{label:"Aporia Configuration",name:"config",tooltip:"JSON configuration for Aporia",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ "api_key": "your_aporia_api_key", "project_name": "your_project_name" }`})});case"AimSecurity":return(0,l.jsx)(u.Form.Item,{label:"Aim Security Configuration",name:"config",tooltip:"JSON configuration for Aim Security",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ @@ -18,7 +18,7 @@ }`})});default:return(0,l.jsx)(u.Form.Item,{label:"Custom Configuration",name:"config",tooltip:"JSON configuration for your custom guardrail",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ "key1": "value1", "key2": "value2" -}`})})}})(),(0,l.jsxs)("div",{className:"flex justify-end space-x-2 mt-4",children:[(0,l.jsx)(e9.Button,{variant:"secondary",onClick:t,children:"Cancel"}),(0,l.jsx)(e9.Button,{onClick:I,loading:c,children:"Update Guardrail"})]})]})})};var tu=((a={}).DB="db",a.CONFIG="config",a);let tp=({guardrailsList:e,isLoading:t,onDeleteClick:a,accessToken:i,onGuardrailUpdated:s,isAdmin:n=!1,onGuardrailClick:o})=>{let[d,c]=(0,r.useState)([{id:"created_at",desc:!0}]),[m,u]=(0,r.useState)(!1),[p,g]=(0,r.useState)(null),x=e=>e?new Date(e).toLocaleString():"-",h=[{header:"Guardrail ID",accessorKey:"guardrail_id",cell:e=>(0,l.jsx)(eN.Tooltip,{title:String(e.getValue()||""),children:(0,l.jsx)(e9.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate max-w-[200px]",onClick:()=>e.getValue()&&o(e.getValue()),children:e.getValue()?`${String(e.getValue()).slice(0,7)}...`:""})})},{header:"Name",accessorKey:"guardrail_name",cell:({row:e})=>{let t=e.original;return(0,l.jsx)(eN.Tooltip,{title:t.guardrail_name,children:(0,l.jsx)("span",{className:"text-xs font-medium",children:t.guardrail_name||"-"})})}},{header:"Provider",accessorKey:"litellm_params.guardrail",cell:({row:e})=>{let{logo:t,displayName:a}=eg(e.original.litellm_params.guardrail);return(0,l.jsxs)("div",{className:"flex items-center space-x-2",children:[t&&(0,l.jsx)("img",{src:t,alt:`${a} logo`,className:"w-4 h-4",onError:e=>{e.target.style.display="none"}}),(0,l.jsx)("span",{className:"text-xs",children:a})]})}},{header:"Mode",accessorKey:"litellm_params.mode",cell:({row:e})=>{let t=e.original;return(0,l.jsx)("span",{className:"text-xs",children:t.litellm_params.mode})}},{header:"Default On",accessorKey:"litellm_params.default_on",cell:({row:e})=>{let t=e.original;return(0,l.jsx)(tr.Badge,{color:t.litellm_params?.default_on?"green":"gray",className:"text-xs font-normal",size:"xs",children:t.litellm_params?.default_on?"Default On":"Default Off"})}},{header:"Created At",accessorKey:"created_at",cell:({row:e})=>{let t=e.original;return(0,l.jsx)(eN.Tooltip,{title:t.created_at,children:(0,l.jsx)("span",{className:"text-xs",children:x(t.created_at)})})}},{header:"Updated At",accessorKey:"updated_at",cell:({row:e})=>{let t=e.original;return(0,l.jsx)(eN.Tooltip,{title:t.updated_at,children:(0,l.jsx)("span",{className:"text-xs",children:x(t.updated_at)})})}},{id:"actions",header:"Actions",cell:({row:e})=>{let t=e.original,r=t.guardrail_definition_location===tu.CONFIG;return(0,l.jsx)("div",{className:"flex space-x-2",children:r?(0,l.jsx)(eN.Tooltip,{title:"Config guardrail cannot be deleted on the dashboard. Please delete it from the config file.",children:(0,l.jsx)(e7.Icon,{"data-testid":"config-delete-icon",icon:te.TrashIcon,size:"sm",className:"cursor-not-allowed text-gray-400",title:"Config guardrail cannot be deleted on the dashboard. Please delete it from the config file.","aria-label":"Delete guardrail (config)"})}):(0,l.jsx)(eN.Tooltip,{title:"Delete guardrail",children:(0,l.jsx)(e7.Icon,{icon:te.TrashIcon,size:"sm",onClick:()=>t.guardrail_id&&a(t.guardrail_id,t.guardrail_name||"Unnamed Guardrail"),className:"cursor-pointer hover:text-red-500"})})})}}],f=(0,ti.useReactTable)({data:e,columns:h,state:{sorting:d},onSortingChange:c,getCoreRowModel:(0,ts.getCoreRowModel)(),getSortedRowModel:(0,ts.getSortedRowModel)(),enableSorting:!0});return(0,l.jsxs)("div",{className:"rounded-lg custom-border relative",children:[(0,l.jsx)("div",{className:"overflow-x-auto",children:(0,l.jsxs)(e2.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,l.jsx)(e8.TableHead,{children:f.getHeaderGroups().map(e=>(0,l.jsx)(e3.TableRow,{children:e.headers.map(e=>(0,l.jsx)(e6.TableHeaderCell,{className:`py-1 h-8 ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,onClick:e.column.getToggleSortingHandler(),children:(0,l.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,l.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,ti.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&(0,l.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,l.jsx)(ta.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,l.jsx)(tl.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,l.jsx)(tt.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]})},e.id))},e.id))}),(0,l.jsx)(e4.TableBody,{children:t?(0,l.jsx)(e3.TableRow,{children:(0,l.jsx)(e5.TableCell,{colSpan:h.length,className:"h-8 text-center",children:(0,l.jsx)("div",{className:"text-center text-gray-500",children:(0,l.jsx)("p",{children:"Loading..."})})})}):e.length>0?f.getRowModel().rows.map(e=>(0,l.jsx)(e3.TableRow,{className:"h-8",children:e.getVisibleCells().map(e=>(0,l.jsx)(e5.TableCell,{className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,children:(0,ti.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,l.jsx)(e3.TableRow,{children:(0,l.jsx)(e5.TableCell,{colSpan:h.length,className:"h-8 text-center",children:(0,l.jsx)("div",{className:"text-center text-gray-500",children:(0,l.jsx)("p",{children:"No guardrails found"})})})})})]})}),p&&(0,l.jsx)(tm,{visible:m,onClose:()=>u(!1),accessToken:i,onSuccess:()=>{u(!1),g(null),s()},guardrailId:p.guardrail_id||"",fullLitellmParams:p.litellm_params,initialValues:{guardrail_name:p.guardrail_name||"",provider:Object.keys(en).find(e=>en[e]===p?.litellm_params.guardrail)||"",mode:p.litellm_params.mode,default_on:p.litellm_params.default_on,pii_entities_config:p.litellm_params.pii_entities_config,skip_system_message_choice:ex(p.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:eh(p.litellm_params?.skip_tool_message_in_guardrail),...p.guardrail_info}})]})};var tg=e.i(708347),tx=e.i(500330),eA=eA,th=e.i(530212),tf=e.i(350967),ty=e.i(197647),tj=e.i(653824),t_=e.i(881073),tb=e.i(404206),tv=e.i(723731),tw=e.i(629569),tN=e.i(678784),tC=e.i(118366),tS=e.i(560445);let{Text:tk}=f.Typography,{Option:tI}=x.Select,tA=({categories:e,onActionChange:t,onSeverityChange:a,onRemove:r,readOnly:s=!1})=>{let n=[{title:"Category",dataIndex:"display_name",key:"display_name",render:(e,t)=>(0,l.jsxs)("div",{children:[(0,l.jsx)(tk,{strong:!0,children:e}),e!==t.category&&(0,l.jsx)("div",{children:(0,l.jsx)(tk,{type:"secondary",style:{fontSize:12},children:t.category})})]})},{title:"Severity Threshold",dataIndex:"severity_threshold",key:"severity_threshold",width:180,render:(e,t)=>s?(0,l.jsx)(h.Tag,{color:{high:"red",medium:"orange",low:"yellow"}[e],children:e.toUpperCase()}):(0,l.jsxs)(x.Select,{value:e,onChange:e=>a?.(t.id,e),style:{width:150},size:"small",children:[(0,l.jsx)(tI,{value:"high",children:"High"}),(0,l.jsx)(tI,{value:"medium",children:"Medium"}),(0,l.jsx)(tI,{value:"low",children:"Low"})]})},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>s?(0,l.jsx)(h.Tag,{color:"BLOCK"===e?"red":"blue",children:e}):(0,l.jsxs)(x.Select,{value:e,onChange:e=>t?.(a.id,e),style:{width:120},size:"small",children:[(0,l.jsx)(tI,{value:"BLOCK",children:"Block"}),(0,l.jsx)(tI,{value:"MASK",children:"Mask"})]})}];return(s||n.push({title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>r?.(t.id),children:"Delete"})}),0===e.length)?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No categories configured."}):(0,l.jsx)(P.Table,{dataSource:e,columns:n,rowKey:"id",pagination:!1,size:"small"})},tO=({patterns:e,blockedWords:t,categories:a=[],readOnly:r=!0,onPatternActionChange:i,onPatternRemove:s,onBlockedWordUpdate:n,onBlockedWordRemove:o,onCategoryActionChange:d,onCategorySeverityChange:c,onCategoryRemove:m})=>{if(0===e.length&&0===t.length&&0===a.length)return null;let u=()=>{};return(0,l.jsxs)(l.Fragment,{children:[a.length>0&&(0,l.jsxs)(eK.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eq.Text,{className:"text-lg font-semibold",children:"Content Categories"}),(0,l.jsxs)(tr.Badge,{color:"blue",children:[a.length," categories configured"]})]}),(0,l.jsx)(tA,{categories:a,onActionChange:r?void 0:d,onSeverityChange:r?void 0:c,onRemove:r?void 0:m,readOnly:r})]}),e.length>0&&(0,l.jsxs)(eK.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eq.Text,{className:"text-lg font-semibold",children:"Pattern Detection"}),(0,l.jsxs)(tr.Badge,{color:"blue",children:[e.length," patterns configured"]})]}),(0,l.jsx)($,{patterns:e,onActionChange:r?u:i||u,onRemove:r?u:s||u})]}),t.length>0&&(0,l.jsxs)(eK.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eq.Text,{className:"text-lg font-semibold",children:"Blocked Keywords"}),(0,l.jsxs)(tr.Badge,{color:"blue",children:[t.length," keywords configured"]})]}),(0,l.jsx)(R,{keywords:t,onActionChange:r?u:n||u,onRemove:r?u:o||u})]})]})},{Text:tT}=f.Typography,tP=({guardrailData:e,guardrailSettings:t,isEditing:a,accessToken:i,onDataChange:s,onUnsavedChanges:n})=>{let[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[u,p]=(0,r.useState)([]),[g,x]=(0,r.useState)([]),[h,f]=(0,r.useState)([]),[y,j]=(0,r.useState)([]),[_,b]=(0,r.useState)(!1),[v,w]=(0,r.useState)(null),[N,C]=(0,r.useState)(!1),[S,k]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(e?.litellm_params?.patterns){let t=e.litellm_params.patterns.map((e,t)=>({id:`pattern-${t}`,type:"prebuilt"===e.pattern_type?"prebuilt":"custom",name:e.pattern_name||e.name,display_name:e.display_name,pattern:e.pattern,action:e.action||"BLOCK"}));d(t),x(t)}else d([]),x([]);if(e?.litellm_params?.blocked_words){let t=e.litellm_params.blocked_words.map((e,t)=>({id:`word-${t}`,keyword:e.keyword,action:e.action||"BLOCK",description:e.description}));m(t),f(t)}else m([]),f([]);if(e?.litellm_params?.categories?.length>0){let a=t?.content_filter_settings?.content_categories?Object.fromEntries(t.content_filter_settings.content_categories.map(e=>[e.name,e])):{},l=e.litellm_params.categories.map((e,t)=>{let l=a[e.category];return{id:`category-${t}`,category:e.category,display_name:l?.display_name??e.category,action:e.action||"BLOCK",severity_threshold:e.severity_threshold||"medium"}});p(l),j(l)}else p([]),j([]);let a=e?.litellm_params?.competitor_intent_config;if(a&&"object"==typeof a){let e=!!(a.brand_self&&Array.isArray(a.brand_self)&&a.brand_self.length>0),t={competitor_intent_type:a.competitor_intent_type??"airline",brand_self:Array.isArray(a.brand_self)?a.brand_self:[],locations:Array.isArray(a.locations)?a.locations:[],competitors:Array.isArray(a.competitors)?a.competitors:[],policy:a.policy??{competitor_comparison:"refuse",possible_competitor_comparison:"reframe"},threshold_high:"number"==typeof a.threshold_high?a.threshold_high:.7,threshold_medium:"number"==typeof a.threshold_medium?a.threshold_medium:.45,threshold_low:"number"==typeof a.threshold_low?a.threshold_low:.3};b(e),w(t),C(e),k(t)}else b(!1),w(null),C(!1),k(null)},[e,t?.content_filter_settings?.content_categories]),(0,r.useEffect)(()=>{s&&s(o,c,u,_,v)},[o,c,u,_,v,s]);let I=r.default.useMemo(()=>{let e=JSON.stringify(o)!==JSON.stringify(g),t=JSON.stringify(c)!==JSON.stringify(h),a=JSON.stringify(u)!==JSON.stringify(y),l=_!==N||JSON.stringify(v)!==JSON.stringify(S);return e||t||a||l},[o,c,u,_,v,g,h,y,N,S]);return((0,r.useEffect)(()=>{a&&n&&n(I)},[I,a,n]),e?.litellm_params?.guardrail!=="litellm_content_filter")?null:a?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eH.Divider,{orientation:"left",children:"Content Filter Configuration"}),I&&(0,l.jsx)(tS.Alert,{type:"warning",showIcon:!0,className:"mb-4",message:(0,l.jsx)(tT,{children:'You have unsaved changes to patterns or keywords. Remember to click "Save Changes" at the bottom.'})}),(0,l.jsx)("div",{className:"mb-6",children:t&&t.content_filter_settings&&(0,l.jsx)(et,{prebuiltPatterns:t.content_filter_settings.prebuilt_patterns||[],categories:t.content_filter_settings.pattern_categories||[],selectedPatterns:o,blockedWords:c,onPatternAdd:e=>d([...o,e]),onPatternRemove:e=>d(o.filter(t=>t.id!==e)),onPatternActionChange:(e,t)=>d(o.map(a=>a.id===e?{...a,action:t}:a)),onBlockedWordAdd:e=>m([...c,e]),onBlockedWordRemove:e=>m(c.filter(t=>t.id!==e)),onBlockedWordUpdate:(e,t,a)=>m(c.map(l=>l.id===e?{...l,[t]:a}:l)),onFileUpload:e=>{},accessToken:i,contentCategories:t.content_filter_settings.content_categories||[],selectedContentCategories:u,onContentCategoryAdd:e=>p([...u,e]),onContentCategoryRemove:e=>p(u.filter(t=>t.id!==e)),onContentCategoryUpdate:(e,t,a)=>p(u.map(l=>l.id===e?{...l,[t]:a}:l)),competitorIntentEnabled:_,competitorIntentConfig:v,onCompetitorIntentChange:(e,t)=>{b(e),w(t)}})})]}):(0,l.jsx)(tO,{patterns:o,blockedWords:c,categories:u,readOnly:!0})};var tL=e.i(788191),tB=e.i(245704),tF=e.i(518617);let t$={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M715.8 493.5L335 165.1c-14.2-12.2-35-1.2-35 18.5v656.8c0 19.7 20.8 30.7 35 18.5l380.8-328.4c10.9-9.4 10.9-27.6 0-37z"}}]},name:"caret-right",theme:"outlined"};var tE=r.forwardRef(function(e,t){return r.createElement(eL.default,(0,eT.default)({},e,{ref:t,icon:t$}))}),tM=e.i(987432);let tR={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M892 772h-80v-80c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v80h-80c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h80v80c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-80h80c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM373.5 498.4c-.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1a127.54 127.54 0 01-38.7-95.4c.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.8-1.7-203.2 89.2-203.2 200 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204a8 8 0 008 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.8-1.1 6.4-4.8 5.9-8.8zM824 472c0-109.4-87.9-198.3-196.9-200C516.3 270.3 424 361.2 424 472c0 62.8 29 118.8 74.2 155.5a300.95 300.95 0 00-86.4 60.4C357 742.6 326 814.8 324 891.8a8 8 0 008 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5C505.8 695.7 563 672 624 672c110.4 0 200-89.5 200-200zm-109.5 90.5C690.3 586.7 658.2 600 624 600s-66.3-13.3-90.5-37.5a127.26 127.26 0 01-37.5-91.8c.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4-.1 34.2-13.4 66.3-37.6 90.5z"}}]},name:"usergroup-add",theme:"outlined"};var tG=r.forwardRef(function(e,t){return r.createElement(eL.default,(0,eT.default)({},e,{ref:t,icon:tR}))}),tz=e.i(872934);let{Panel:tD}=G.Collapse,{TextArea:tK}=p.Input,tq={empty:{name:"Empty Template",code:`async def apply_guardrail(inputs, request_data, input_type): +}`})})}})(),(0,l.jsxs)("div",{className:"flex justify-end space-x-2 mt-4",children:[(0,l.jsx)(tm.Button,{variant:"secondary",onClick:t,children:"Cancel"}),(0,l.jsx)(tm.Button,{onClick:O,loading:f,children:"Update Guardrail"})]})]})})};var tf=((a={}).DB="db",a.CONFIG="config",a);let ty=({guardrailsList:e,isLoading:t,onDeleteClick:a,accessToken:i,onGuardrailUpdated:s,isAdmin:n=!1,onGuardrailClick:o})=>{let[d,c]=(0,r.useState)([{id:"created_at",desc:!0}]),[m,u]=(0,r.useState)(!1),[p,g]=(0,r.useState)(null),x=[{header:"Guardrail ID",accessorKey:"guardrail_id",cell:e=>(0,l.jsx)(tn.IdCell,{value:e.getValue(),onClick:o})},{header:"Name",accessorKey:"guardrail_name",cell:({row:e})=>{let t=e.original;return(0,l.jsx)(ek.Tooltip,{title:t.guardrail_name,children:(0,l.jsx)("span",{className:"text-xs font-medium",children:t.guardrail_name||"-"})})}},{header:"Provider",accessorKey:"litellm_params.guardrail",cell:({row:e})=>{let{logo:t,displayName:a}=eh(e.original.litellm_params.guardrail);return(0,l.jsxs)("div",{className:"flex items-center space-x-2",children:[t&&(0,l.jsx)("img",{src:t,alt:`${a} logo`,className:"w-4 h-4",onError:e=>{e.target.style.display="none"}}),(0,l.jsx)("span",{className:"text-xs",children:a})]})}},{header:"Mode",accessorKey:"litellm_params.mode",cell:({row:e})=>{let t=e.original;return(0,l.jsx)("span",{className:"text-xs",children:t.litellm_params.mode})}},{header:"Default On",accessorKey:"litellm_params.default_on",cell:({row:e})=>{let t=!!e.original.litellm_params?.default_on;return(0,l.jsx)(to.StatusBadge,{tone:t?"success":"neutral",label:t?"Default On":"Default Off"})}},{header:"Created At",accessorKey:"created_at",cell:({row:e})=>(0,l.jsx)(ts.DateCell,{value:e.original.created_at})},{header:"Updated At",accessorKey:"updated_at",cell:({row:e})=>(0,l.jsx)(ts.DateCell,{value:e.original.updated_at})},{id:"actions",header:"Actions",cell:({row:e})=>{let t=e.original,r=t.guardrail_definition_location===tf.CONFIG;return(0,l.jsx)("div",{className:"flex space-x-2",children:r?(0,l.jsx)(ek.Tooltip,{title:"Config guardrail cannot be deleted on the dashboard. Please delete it from the config file.",children:(0,l.jsx)(tt.Icon,{"data-testid":"config-delete-icon",icon:ta.TrashIcon,size:"sm",className:"cursor-not-allowed text-gray-400",title:"Config guardrail cannot be deleted on the dashboard. Please delete it from the config file.","aria-label":"Delete guardrail (config)"})}):(0,l.jsx)(ek.Tooltip,{title:"Delete guardrail",children:(0,l.jsx)(tt.Icon,{icon:ta.TrashIcon,size:"sm",onClick:()=>t.guardrail_id&&a(t.guardrail_id,t.guardrail_name||"Unnamed Guardrail"),className:"cursor-pointer hover:text-red-500"})})})}}],h=(0,td.useReactTable)({data:e,columns:x,state:{sorting:d},onSortingChange:c,getCoreRowModel:(0,tc.getCoreRowModel)(),getSortedRowModel:(0,tc.getSortedRowModel)(),enableSorting:!0});return(0,l.jsxs)("div",{className:"rounded-lg custom-border relative",children:[(0,l.jsx)("div",{className:"overflow-x-auto",children:(0,l.jsxs)(e8.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,l.jsx)(e7.TableHead,{children:h.getHeaderGroups().map(e=>(0,l.jsx)(te.TableRow,{children:e.headers.map(e=>(0,l.jsx)(e9.TableHeaderCell,{className:`py-1 h-8 ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,onClick:e.column.getToggleSortingHandler(),children:(0,l.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,l.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,td.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&(0,l.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,l.jsx)(tr.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,l.jsx)(ti.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,l.jsx)(tl.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]})},e.id))},e.id))}),(0,l.jsx)(e6.TableBody,{children:t?(0,l.jsx)(te.TableRow,{children:(0,l.jsx)(e3.TableCell,{colSpan:x.length,className:"h-8 text-center",children:(0,l.jsx)("div",{className:"text-center text-gray-500",children:(0,l.jsx)("p",{children:"Loading..."})})})}):e.length>0?h.getRowModel().rows.map(e=>(0,l.jsx)(te.TableRow,{className:"h-8",children:e.getVisibleCells().map(e=>(0,l.jsx)(e3.TableCell,{className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,children:(0,td.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,l.jsx)(te.TableRow,{children:(0,l.jsx)(e3.TableCell,{colSpan:x.length,className:"h-8 text-center",children:(0,l.jsx)("div",{className:"text-center text-gray-500",children:(0,l.jsx)("p",{children:"No guardrails found"})})})})})]})}),p&&(0,l.jsx)(th,{visible:m,onClose:()=>u(!1),accessToken:i,onSuccess:()=>{u(!1),g(null),s()},guardrailId:p.guardrail_id||"",fullLitellmParams:p.litellm_params,initialValues:{guardrail_name:p.guardrail_name||"",provider:Object.keys(en).find(e=>en[e]===p?.litellm_params.guardrail)||"",mode:p.litellm_params.mode,default_on:p.litellm_params.default_on,pii_entities_config:p.litellm_params.pii_entities_config,skip_system_message_choice:ef(p.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:ey(p.litellm_params?.skip_tool_message_in_guardrail),...p.guardrail_info}})]})};var tj=e.i(708347),t_=e.i(500330),eT=eT,tb=e.i(530212),tv=e.i(389083),tw=e.i(350967),tC=e.i(197647),tN=e.i(653824),tk=e.i(881073),tS=e.i(404206),tI=e.i(723731),tA=e.i(629569),tO=e.i(678784),tT=e.i(118366),tP=e.i(560445);let{Text:tL}=f.Typography,{Option:tB}=x.Select,tF=({categories:e,onActionChange:t,onSeverityChange:a,onRemove:r,readOnly:s=!1})=>{let n=[{title:"Category",dataIndex:"display_name",key:"display_name",render:(e,t)=>(0,l.jsxs)("div",{children:[(0,l.jsx)(tL,{strong:!0,children:e}),e!==t.category&&(0,l.jsx)("div",{children:(0,l.jsx)(tL,{type:"secondary",style:{fontSize:12},children:t.category})})]})},{title:"Severity Threshold",dataIndex:"severity_threshold",key:"severity_threshold",width:180,render:(e,t)=>s?(0,l.jsx)(h.Tag,{color:{high:"red",medium:"orange",low:"yellow"}[e],children:e.toUpperCase()}):(0,l.jsxs)(x.Select,{value:e,onChange:e=>a?.(t.id,e),style:{width:150},size:"small",children:[(0,l.jsx)(tB,{value:"high",children:"High"}),(0,l.jsx)(tB,{value:"medium",children:"Medium"}),(0,l.jsx)(tB,{value:"low",children:"Low"})]})},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>s?(0,l.jsx)(h.Tag,{color:"BLOCK"===e?"red":"blue",children:e}):(0,l.jsxs)(x.Select,{value:e,onChange:e=>t?.(a.id,e),style:{width:120},size:"small",children:[(0,l.jsx)(tB,{value:"BLOCK",children:"Block"}),(0,l.jsx)(tB,{value:"MASK",children:"Mask"})]})}];return(s||n.push({title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>r?.(t.id),children:"Delete"})}),0===e.length)?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No categories configured."}):(0,l.jsx)(P.Table,{dataSource:e,columns:n,rowKey:"id",pagination:!1,size:"small"})},t$=({patterns:e,blockedWords:t,categories:a=[],readOnly:r=!0,onPatternActionChange:i,onPatternRemove:s,onBlockedWordUpdate:n,onBlockedWordRemove:o,onCategoryActionChange:d,onCategorySeverityChange:c,onCategoryRemove:m})=>{if(0===e.length&&0===t.length&&0===a.length)return null;let u=()=>{};return(0,l.jsxs)(l.Fragment,{children:[a.length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"Content Categories"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[a.length," categories configured"]})]}),(0,l.jsx)(tF,{categories:a,onActionChange:r?void 0:d,onSeverityChange:r?void 0:c,onRemove:r?void 0:m,readOnly:r})]}),e.length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"Pattern Detection"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[e.length," patterns configured"]})]}),(0,l.jsx)($,{patterns:e,onActionChange:r?u:i||u,onRemove:r?u:s||u})]}),t.length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"Blocked Keywords"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[t.length," keywords configured"]})]}),(0,l.jsx)(R,{keywords:t,onActionChange:r?u:n||u,onRemove:r?u:o||u})]})]})},{Text:tE}=f.Typography,tM=({guardrailData:e,guardrailSettings:t,isEditing:a,accessToken:i,onDataChange:s,onUnsavedChanges:n})=>{let[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[u,p]=(0,r.useState)([]),[g,x]=(0,r.useState)([]),[h,f]=(0,r.useState)([]),[y,j]=(0,r.useState)([]),[_,b]=(0,r.useState)(!1),[v,w]=(0,r.useState)(null),[C,N]=(0,r.useState)(!1),[k,S]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(e?.litellm_params?.patterns){let t=e.litellm_params.patterns.map((e,t)=>({id:`pattern-${t}`,type:"prebuilt"===e.pattern_type?"prebuilt":"custom",name:e.pattern_name||e.name,display_name:e.display_name,pattern:e.pattern,action:e.action||"BLOCK"}));d(t),x(t)}else d([]),x([]);if(e?.litellm_params?.blocked_words){let t=e.litellm_params.blocked_words.map((e,t)=>({id:`word-${t}`,keyword:e.keyword,action:e.action||"BLOCK",description:e.description}));m(t),f(t)}else m([]),f([]);if(e?.litellm_params?.categories?.length>0){let a=t?.content_filter_settings?.content_categories?Object.fromEntries(t.content_filter_settings.content_categories.map(e=>[e.name,e])):{},l=e.litellm_params.categories.map((e,t)=>{let l=a[e.category];return{id:`category-${t}`,category:e.category,display_name:l?.display_name??e.category,action:e.action||"BLOCK",severity_threshold:e.severity_threshold||"medium"}});p(l),j(l)}else p([]),j([]);let a=e?.litellm_params?.competitor_intent_config;if(a&&"object"==typeof a){let e=!!(a.brand_self&&Array.isArray(a.brand_self)&&a.brand_self.length>0),t={competitor_intent_type:a.competitor_intent_type??"airline",brand_self:Array.isArray(a.brand_self)?a.brand_self:[],locations:Array.isArray(a.locations)?a.locations:[],competitors:Array.isArray(a.competitors)?a.competitors:[],policy:a.policy??{competitor_comparison:"refuse",possible_competitor_comparison:"reframe"},threshold_high:"number"==typeof a.threshold_high?a.threshold_high:.7,threshold_medium:"number"==typeof a.threshold_medium?a.threshold_medium:.45,threshold_low:"number"==typeof a.threshold_low?a.threshold_low:.3};b(e),w(t),N(e),S(t)}else b(!1),w(null),N(!1),S(null)},[e,t?.content_filter_settings?.content_categories]),(0,r.useEffect)(()=>{s&&s(o,c,u,_,v)},[o,c,u,_,v,s]);let I=r.default.useMemo(()=>{let e=JSON.stringify(o)!==JSON.stringify(g),t=JSON.stringify(c)!==JSON.stringify(h),a=JSON.stringify(u)!==JSON.stringify(y),l=_!==C||JSON.stringify(v)!==JSON.stringify(k);return e||t||a||l},[o,c,u,_,v,g,h,y,C,k]);return((0,r.useEffect)(()=>{a&&n&&n(I)},[I,a,n]),e?.litellm_params?.guardrail!=="litellm_content_filter")?null:a?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eJ.Divider,{orientation:"left",children:"Content Filter Configuration"}),I&&(0,l.jsx)(tP.Alert,{type:"warning",showIcon:!0,className:"mb-4",message:(0,l.jsx)(tE,{children:'You have unsaved changes to patterns or keywords. Remember to click "Save Changes" at the bottom.'})}),(0,l.jsx)("div",{className:"mb-6",children:t&&t.content_filter_settings&&(0,l.jsx)(et,{prebuiltPatterns:t.content_filter_settings.prebuilt_patterns||[],categories:t.content_filter_settings.pattern_categories||[],selectedPatterns:o,blockedWords:c,onPatternAdd:e=>d([...o,e]),onPatternRemove:e=>d(o.filter(t=>t.id!==e)),onPatternActionChange:(e,t)=>d(o.map(a=>a.id===e?{...a,action:t}:a)),onBlockedWordAdd:e=>m([...c,e]),onBlockedWordRemove:e=>m(c.filter(t=>t.id!==e)),onBlockedWordUpdate:(e,t,a)=>m(c.map(l=>l.id===e?{...l,[t]:a}:l)),onFileUpload:e=>{},accessToken:i,contentCategories:t.content_filter_settings.content_categories||[],selectedContentCategories:u,onContentCategoryAdd:e=>p([...u,e]),onContentCategoryRemove:e=>p(u.filter(t=>t.id!==e)),onContentCategoryUpdate:(e,t,a)=>p(u.map(l=>l.id===e?{...l,[t]:a}:l)),competitorIntentEnabled:_,competitorIntentConfig:v,onCompetitorIntentChange:(e,t)=>{b(e),w(t)}})})]}):(0,l.jsx)(t$,{patterns:o,blockedWords:c,categories:u,readOnly:!0})};var tR=e.i(788191),tG=e.i(245704),tz=e.i(518617);let tD={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M715.8 493.5L335 165.1c-14.2-12.2-35-1.2-35 18.5v656.8c0 19.7 20.8 30.7 35 18.5l380.8-328.4c10.9-9.4 10.9-27.6 0-37z"}}]},name:"caret-right",theme:"outlined"};var tK=r.forwardRef(function(e,t){return r.createElement(eF.default,(0,eL.default)({},e,{ref:t,icon:tD}))}),tq=e.i(987432);let tH={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M892 772h-80v-80c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v80h-80c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h80v80c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-80h80c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM373.5 498.4c-.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1a127.54 127.54 0 01-38.7-95.4c.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.8-1.7-203.2 89.2-203.2 200 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204a8 8 0 008 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.8-1.1 6.4-4.8 5.9-8.8zM824 472c0-109.4-87.9-198.3-196.9-200C516.3 270.3 424 361.2 424 472c0 62.8 29 118.8 74.2 155.5a300.95 300.95 0 00-86.4 60.4C357 742.6 326 814.8 324 891.8a8 8 0 008 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5C505.8 695.7 563 672 624 672c110.4 0 200-89.5 200-200zm-109.5 90.5C690.3 586.7 658.2 600 624 600s-66.3-13.3-90.5-37.5a127.26 127.26 0 01-37.5-91.8c.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4-.1 34.2-13.4 66.3-37.6 90.5z"}}]},name:"usergroup-add",theme:"outlined"};var tU=r.forwardRef(function(e,t){return r.createElement(eF.default,(0,eL.default)({},e,{ref:t,icon:tH}))}),tJ=e.i(872934);let{Panel:tW}=G.Collapse,{TextArea:tV}=p.Input,tY={empty:{name:"Empty Template",code:`async def apply_guardrail(inputs, request_data, input_type): # inputs: {texts, images, tools, tool_calls, structured_messages, model} # request_data: {model, user_id, team_id, end_user_id, metadata} # input_type: "request" or "response" @@ -66,7 +66,7 @@ if response["body"].get("flagged"): return block(response["body"].get("reason", "Content flagged")) - return allow()`}},tH={"Return Values":[{name:"allow()",desc:"Let request/response through"},{name:"block(reason)",desc:"Reject with message"},{name:"modify(texts=[], images=[], tool_calls=[])",desc:"Transform content"}],"HTTP Requests (async)":[{name:"await http_request(url, method, headers, body)",desc:"Make async HTTP request"},{name:"await http_get(url, headers)",desc:"Async GET request"},{name:"await http_post(url, body, headers)",desc:"Async POST request"}],"Regex Functions":[{name:"regex_match(text, pattern)",desc:"Returns True if pattern found"},{name:"regex_replace(text, pattern, replacement)",desc:"Replace all matches"},{name:"regex_find_all(text, pattern)",desc:"Return list of matches"}],"JSON Functions":[{name:"json_parse(text)",desc:"Parse JSON string, returns None on error"},{name:"json_stringify(obj)",desc:"Convert to JSON string"},{name:"json_schema_valid(obj, schema)",desc:"Validate against JSON schema"}],"URL Functions":[{name:"extract_urls(text)",desc:"Extract all URLs from text"},{name:"is_valid_url(url)",desc:"Check if URL is valid"},{name:"all_urls_valid(text)",desc:"Check all URLs in text are valid"}],"Code Detection":[{name:"detect_code(text)",desc:"Returns True if code detected"},{name:"detect_code_languages(text)",desc:"Returns list of detected languages"},{name:'contains_code_language(text, ["sql"])',desc:"Check for specific languages"}],"Text Utilities":[{name:"contains(text, substring)",desc:"Check if substring exists"},{name:"contains_any(text, [substr1, substr2])",desc:"Check if any substring exists"},{name:"word_count(text)",desc:"Count words"},{name:"char_count(text)",desc:"Count characters"},{name:"lower(text) / upper(text) / trim(text)",desc:"String transforms"}]},tU=[{value:"pre_call",label:"pre_call (Request)"},{value:"post_call",label:"post_call (Response)"},{value:"during_call",label:"during_call (Parallel)"},{value:"logging_only",label:"logging_only"},{value:"pre_mcp_call",label:"pre_mcp_call (Before MCP Tool Call)"},{value:"post_mcp_call",label:"post_mcp_call (After MCP Tool Call)"},{value:"during_mcp_call",label:"during_mcp_call (During MCP Tool Call)"}],tJ=({visible:e,onClose:t,onSuccess:a,accessToken:i,editData:s})=>{let n=!!s,[o,d]=(0,r.useState)(""),[u,p]=(0,r.useState)(["pre_call"]),[h,f]=(0,r.useState)(!1),[j,_]=(0,r.useState)("empty"),[b,v]=(0,r.useState)(tq.empty.code),[w,N]=(0,r.useState)(!1),[C,S]=(0,r.useState)(!1),[k,I]=(0,r.useState)(!1),A={texts:["Hello, my SSN is 123-45-6789"],images:[],tools:[{type:"function",function:{name:"get_weather",description:"Get the current weather in a location",parameters:{type:"object",properties:{location:{type:"string",description:"City name"}},required:["location"]}}}],tool_calls:[],structured_messages:[{role:"system",content:"You are a helpful assistant."},{role:"user",content:"Hello, my SSN is 123-45-6789"}],model:"gpt-4"},O={texts:["The weather in San Francisco is 72°F and sunny."],images:[],tools:[],tool_calls:[{id:"call_abc123",type:"function",function:{name:"get_weather",arguments:'{"location": "San Francisco"}'}}],structured_messages:[],model:"gpt-4"},T={texts:['Tool: read_wiki_structure\nArguments: {"repoName": "BerriAI/litellm"}'],images:[],tools:[{type:"function",function:{name:"read_wiki_structure",description:"Read the structure of a GitHub repository (MCP tool passed as OpenAI tool)",parameters:{type:"object",properties:{repoName:{type:"string",description:"Repository name, e.g. BerriAI/litellm"}},required:["repoName"]}}}],tool_calls:[{id:"call_mcp_001",type:"function",function:{name:"read_wiki_structure",arguments:'{"repoName": "BerriAI/litellm"}'}}],structured_messages:[{role:"user",content:'Tool: read_wiki_structure\nArguments: {"repoName": "BerriAI/litellm"}'}],model:"mcp-tool-call"},[P,L]=(0,r.useState)(JSON.stringify(A,null,2)),[B,F]=(0,r.useState)(null),[$,E]=(0,r.useState)(null),M=(0,r.useRef)(null),R=e=>null==e?["pre_call"]:Array.isArray(e)?e.length?e:["pre_call"]:[e];(0,r.useEffect)(()=>{e&&(s?(d(s.guardrail_name||""),p(R(s.litellm_params?.mode)),f(s.litellm_params?.default_on||!1),v(s.litellm_params?.custom_code||tq.empty.code),_("")):(d(""),p(["pre_call"]),f(!1),_("empty"),v(tq.empty.code)),F(null),I(!1))},[e,s]);let z=async e=>{try{await navigator.clipboard.writeText(e),E(e),setTimeout(()=>E(null),2e3)}catch(e){console.error("Failed to copy:",e)}},D=async()=>{if(!o.trim())return void y.default.fromBackend("Please enter a guardrail name");if(!b.trim())return void y.default.fromBackend("Please enter custom code");if(!i)return void y.default.fromBackend("No access token available");N(!0);try{if(n&&s){let e={litellm_params:{custom_code:b}};o!==s.guardrail_name&&(e.guardrail_name=o);let t=R(s.litellm_params?.mode);(u.length!==t.length||u.some((e,a)=>e!==t[a]))&&(e.litellm_params.mode=u),h!==s.litellm_params?.default_on&&(e.litellm_params.default_on=h),await (0,m.updateGuardrailCall)(i,s.guardrail_id,e),y.default.success("Custom code guardrail updated successfully")}else await (0,m.createGuardrailCall)(i,{guardrail_name:o,litellm_params:{guardrail:"custom_code",mode:u,default_on:h,custom_code:b},guardrail_info:{}}),y.default.success("Custom code guardrail created successfully");a(),t()}catch(e){console.error("Failed to save guardrail:",e),y.default.fromBackend(`Failed to ${n?"update":"create"} guardrail: `+(e instanceof Error?e.message:String(e)))}finally{N(!1)}},K=async()=>{if(!i)return void F({error:"No access token available"});S(!0),F(null);try{let e;try{e=JSON.parse(P)}catch(e){F({error:"Invalid test input JSON"}),S(!1);return}e.texts||(e.texts=[]);let t=["pre_call","pre_mcp_call"],a=["post_call","post_mcp_call"],l=u.some(e=>t.includes(e))?"request":u.some(e=>a.includes(e))?"response":"request",r=await (0,m.testCustomCodeGuardrail)(i,{custom_code:b,test_input:e,input_type:l,request_data:{model:"test-model",metadata:{}}});r.success&&r.result?F(r.result):r.error?F({error:r.error,error_type:r.error_type}):F({error:"Unknown error occurred"})}catch(e){console.error("Failed to test custom code:",e),F({error:e instanceof Error?e.message:"Failed to test custom code"})}finally{S(!1)}},q=b.split("\n").length;return(0,l.jsxs)(g.Modal,{open:e,onCancel:t,footer:null,width:1400,className:"custom-code-modal",closable:!0,destroyOnClose:!0,children:[(0,l.jsxs)("div",{className:"flex flex-col h-[80vh]",children:[(0,l.jsxs)("div",{className:"pb-4 border-b border-gray-200",children:[(0,l.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:n?"Edit Custom Guardrail":"Create Custom Guardrail"}),(0,l.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"Define custom logic using Python-like syntax"})]}),(0,l.jsxs)("div",{className:"flex items-center gap-4 py-4 border-b border-gray-100",children:[(0,l.jsxs)("div",{className:"flex-1 max-w-[200px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Guardrail Name"}),(0,l.jsx)(tn.TextInput,{value:o,onValueChange:d,placeholder:"e.g., block-pii-custom"})]}),(0,l.jsxs)("div",{className:"w-[280px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Mode (can select multiple)"}),(0,l.jsx)(x.Select,{mode:"multiple",value:u,onChange:p,options:tU,className:"w-full",size:"middle",placeholder:"Select modes"})]}),(0,l.jsxs)("div",{className:"w-[180px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Template"}),(0,l.jsx)(x.Select,{value:j,onChange:e=>{_(e),v(tq[e].code)},className:"w-full",size:"middle",dropdownRender:e=>(0,l.jsxs)(l.Fragment,{children:[e,(0,l.jsx)(eH.Divider,{style:{margin:"8px 0"}}),(0,l.jsxs)("div",{style:{padding:"8px 12px",cursor:"pointer",color:"#1890ff",fontSize:"12px",display:"flex",alignItems:"center",gap:"4px"},onClick:e=>{e.preventDefault(),window.open("https://models.litellm.ai/guardrails","_blank")},onMouseEnter:e=>{e.currentTarget.style.backgroundColor="#f0f0f0"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent"},children:[(0,l.jsx)(tG,{}),(0,l.jsx)("span",{children:"Browse Community templates"}),(0,l.jsx)(tz.ExportOutlined,{style:{fontSize:"10px"}})]})]}),children:(0,l.jsx)(x.Select.OptGroup,{label:"STANDARD",children:Object.entries(tq).map(([e,t])=>(0,l.jsx)(x.Select.Option,{value:e,children:t.name},e))})})]}),(0,l.jsxs)("div",{className:"flex items-center gap-2 pt-5",children:[(0,l.jsx)("span",{className:"text-sm text-gray-600",children:"Default On"}),(0,l.jsx)(U.Switch,{checked:h,onChange:f})]})]}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-hidden mt-4 gap-6",children:[(0,l.jsxs)("div",{className:"flex-2 flex flex-col min-w-0 overflow-y-auto",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2 shrink-0",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wide",children:"Python Logic"}),(0,l.jsx)("span",{className:"text-xs text-gray-400",children:"Restricted environment (no imports)"})]}),(0,l.jsxs)("div",{className:"relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e] shrink-0",style:{minHeight:"300px",maxHeight:"400px"},children:[(0,l.jsx)("div",{className:"absolute left-0 top-0 bottom-0 w-12 bg-[#1e1e1e] border-r border-gray-700 text-right pr-3 pt-3 select-none overflow-hidden",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace",fontSize:"14px",lineHeight:"1.6"},children:Array.from({length:Math.max(q,20)},(e,t)=>(0,l.jsx)("div",{className:"text-gray-500 h-[22.4px]",children:t+1},t+1))}),(0,l.jsx)("textarea",{ref:M,value:b,onChange:e=>v(e.target.value),onKeyDown:e=>{if("Tab"===e.key){e.preventDefault();let t=e.currentTarget,a=t.selectionStart,l=t.selectionEnd;v(b.substring(0,a)+" "+b.substring(l)),setTimeout(()=>{t.selectionStart=t.selectionEnd=a+4},0)}},spellCheck:!1,className:"w-full h-full pl-14 pr-4 pt-3 pb-3 resize-none focus:outline-hidden bg-transparent text-gray-200",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace",fontSize:"14px",lineHeight:"1.6",tabSize:4}})]}),(0,l.jsx)(G.Collapse,{activeKey:k?["test"]:[],onChange:e=>I(e.includes("test")),className:"mt-3 bg-white border border-gray-200 rounded-lg shrink-0",expandIcon:({isActive:e})=>(0,l.jsx)(tE,{rotate:90*!!e}),children:(0,l.jsx)(tD,{header:(0,l.jsxs)("span",{className:"flex items-center gap-2 text-sm font-medium",children:[(0,l.jsx)(tL.PlayCircleOutlined,{className:"text-blue-500"}),"Test Your Guardrail"]}),children:(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600",children:"Test Input (JSON)"}),(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"text-xs text-gray-500",children:"Load example:"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(A,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100 transition-colors",children:"Pre-call"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(T,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 transition-colors",children:"Pre MCP"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(O,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-green-200 bg-green-50 text-green-700 hover:bg-green-100 transition-colors",children:"Post-call"})]})]}),(0,l.jsx)("div",{className:"mb-2 p-2 bg-gray-50 rounded-sm text-xs text-gray-600 border border-gray-200",children:(0,l.jsxs)("div",{className:"grid grid-cols-2 gap-x-4 gap-y-1",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"texts"}),": Message content (always)"]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"images"}),": Base64 images (vision)"]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"tools"}),": Tool definitions ",(0,l.jsx)("span",{className:"text-orange-600",children:"(pre_call)"}),", MCP as OpenAI tool ",(0,l.jsx)("span",{className:"text-purple-600",children:"(pre_mcp_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"tool_calls"}),": LLM tool calls"," ",(0,l.jsx)("span",{className:"text-green-600",children:"(post_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"structured_messages"}),": Full messages"," ",(0,l.jsx)("span",{className:"text-orange-600",children:"(pre_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"model"}),": Model name (always)"]})]})}),(0,l.jsx)(tK,{value:P,onChange:e=>L(e.target.value),rows:8,className:"font-mono text-xs",placeholder:'{"texts": ["test message"], ...}'})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)(e9.Button,{size:"xs",onClick:K,disabled:C,icon:tL.PlayCircleOutlined,children:C?"Running...":"Run Test"}),B&&(0,l.jsx)("div",{className:`flex items-center gap-2 text-sm ${B.error?"text-red-600":"allow"===B.action?"text-green-600":"block"===B.action?"text-orange-600":"text-blue-600"}`,children:B.error?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tF.CloseCircleOutlined,{}),(0,l.jsxs)("span",{children:[B.error_type&&(0,l.jsxs)("span",{className:"font-medium",children:["[",B.error_type,"] "]}),B.error]})]}):"allow"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tB.CheckCircleOutlined,{})," Allowed"]}):"block"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tF.CloseCircleOutlined,{})," Blocked: ",B.reason]}):"modify"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tB.CheckCircleOutlined,{})," Modified",B.texts&&B.texts.length>0&&(0,l.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:["→ ",B.texts[0].substring(0,50),B.texts[0].length>50?"...":""]})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tB.CheckCircleOutlined,{})," ",B.action||"Unknown"]})})]})]})},"test")}),(0,l.jsxs)("div",{className:"mt-3 p-4 bg-linear-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-lg flex items-center justify-between shrink-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)("div",{className:"bg-blue-100 rounded-full p-2",children:(0,l.jsx)(tG,{className:"text-blue-600 text-lg"})}),(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"text-sm font-medium text-gray-900",children:"Built a useful guardrail?"}),(0,l.jsx)("div",{className:"text-xs text-gray-600",children:"Share it with the community and help others build faster"})]})]}),(0,l.jsx)(e9.Button,{size:"xs",onClick:()=>window.open("https://github.com/BerriAI/litellm-guardrails","_blank"),icon:tz.ExportOutlined,className:"bg-blue-600 hover:bg-blue-700 text-white border-0",children:"Contribute Template"})]})]}),(0,l.jsxs)("div",{className:"w-[300px] shrink-0 overflow-auto border-l border-gray-200 pl-6",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-3",children:[(0,l.jsx)(c.CodeOutlined,{className:"text-blue-500"}),(0,l.jsx)("span",{className:"font-semibold text-gray-700",children:"Available Primitives"})]}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Click to copy functions to clipboard"}),(0,l.jsx)(G.Collapse,{defaultActiveKey:["Return Values"],className:"primitives-collapse bg-transparent border-0",expandIconPosition:"end",children:Object.entries(tH).map(([e,t])=>(0,l.jsx)(tD,{header:(0,l.jsx)("span",{className:"text-sm font-medium text-gray-700",children:e}),className:"bg-white mb-2 rounded-lg border border-gray-200",children:(0,l.jsx)("div",{className:"space-y-2",children:t.map(e=>(0,l.jsx)("button",{onClick:()=>z(e.name),className:`w-full text-left px-2 py-2 rounded transition-colors ${$===e.name?"bg-green-100":"bg-gray-50 hover:bg-blue-50"}`,children:$===e.name?(0,l.jsxs)("span",{className:"flex items-center gap-1 text-xs font-mono text-green-700",children:[(0,l.jsx)(tB.CheckCircleOutlined,{})," Copied!"]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"text-xs font-mono text-gray-800",children:e.name}),(0,l.jsx)("div",{className:"text-[10px] text-gray-500 mt-0.5",children:e.desc})]})},e.name))})},e))})]})]}),(0,l.jsxs)("div",{className:"flex items-center justify-between pt-4 mt-4 border-t border-gray-200",children:[(0,l.jsx)("span",{className:"text-xs text-gray-400",children:"Changes are auto-saved to local draft"}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)(e9.Button,{variant:"secondary",onClick:t,children:"Cancel"}),(0,l.jsx)(e9.Button,{onClick:D,loading:w,disabled:w||!o.trim(),icon:tM.SaveOutlined,children:n?"Update Guardrail":"Save Guardrail"})]})]})]}),(0,l.jsx)("style",{children:` + return allow()`}},tQ={"Return Values":[{name:"allow()",desc:"Let request/response through"},{name:"block(reason)",desc:"Reject with message"},{name:"modify(texts=[], images=[], tool_calls=[])",desc:"Transform content"}],"HTTP Requests (async)":[{name:"await http_request(url, method, headers, body)",desc:"Make async HTTP request"},{name:"await http_get(url, headers)",desc:"Async GET request"},{name:"await http_post(url, body, headers)",desc:"Async POST request"}],"Regex Functions":[{name:"regex_match(text, pattern)",desc:"Returns True if pattern found"},{name:"regex_replace(text, pattern, replacement)",desc:"Replace all matches"},{name:"regex_find_all(text, pattern)",desc:"Return list of matches"}],"JSON Functions":[{name:"json_parse(text)",desc:"Parse JSON string, returns None on error"},{name:"json_stringify(obj)",desc:"Convert to JSON string"},{name:"json_schema_valid(obj, schema)",desc:"Validate against JSON schema"}],"URL Functions":[{name:"extract_urls(text)",desc:"Extract all URLs from text"},{name:"is_valid_url(url)",desc:"Check if URL is valid"},{name:"all_urls_valid(text)",desc:"Check all URLs in text are valid"}],"Code Detection":[{name:"detect_code(text)",desc:"Returns True if code detected"},{name:"detect_code_languages(text)",desc:"Returns list of detected languages"},{name:'contains_code_language(text, ["sql"])',desc:"Check for specific languages"}],"Text Utilities":[{name:"contains(text, substring)",desc:"Check if substring exists"},{name:"contains_any(text, [substr1, substr2])",desc:"Check if any substring exists"},{name:"word_count(text)",desc:"Count words"},{name:"char_count(text)",desc:"Count characters"},{name:"lower(text) / upper(text) / trim(text)",desc:"String transforms"}]},tX=[{value:"pre_call",label:"pre_call (Request)"},{value:"post_call",label:"post_call (Response)"},{value:"during_call",label:"during_call (Parallel)"},{value:"logging_only",label:"logging_only"},{value:"pre_mcp_call",label:"pre_mcp_call (Before MCP Tool Call)"},{value:"post_mcp_call",label:"post_mcp_call (After MCP Tool Call)"},{value:"during_mcp_call",label:"during_mcp_call (During MCP Tool Call)"}],tZ=({visible:e,onClose:t,onSuccess:a,accessToken:i,editData:s})=>{let n=!!s,[o,d]=(0,r.useState)(""),[u,p]=(0,r.useState)(["pre_call"]),[h,f]=(0,r.useState)(!1),[j,_]=(0,r.useState)("empty"),[b,v]=(0,r.useState)(tY.empty.code),[w,C]=(0,r.useState)(!1),[N,k]=(0,r.useState)(!1),[S,I]=(0,r.useState)(!1),A={texts:["Hello, my SSN is 123-45-6789"],images:[],tools:[{type:"function",function:{name:"get_weather",description:"Get the current weather in a location",parameters:{type:"object",properties:{location:{type:"string",description:"City name"}},required:["location"]}}}],tool_calls:[],structured_messages:[{role:"system",content:"You are a helpful assistant."},{role:"user",content:"Hello, my SSN is 123-45-6789"}],model:"gpt-4"},O={texts:["The weather in San Francisco is 72°F and sunny."],images:[],tools:[],tool_calls:[{id:"call_abc123",type:"function",function:{name:"get_weather",arguments:'{"location": "San Francisco"}'}}],structured_messages:[],model:"gpt-4"},T={texts:['Tool: read_wiki_structure\nArguments: {"repoName": "BerriAI/litellm"}'],images:[],tools:[{type:"function",function:{name:"read_wiki_structure",description:"Read the structure of a GitHub repository (MCP tool passed as OpenAI tool)",parameters:{type:"object",properties:{repoName:{type:"string",description:"Repository name, e.g. BerriAI/litellm"}},required:["repoName"]}}}],tool_calls:[{id:"call_mcp_001",type:"function",function:{name:"read_wiki_structure",arguments:'{"repoName": "BerriAI/litellm"}'}}],structured_messages:[{role:"user",content:'Tool: read_wiki_structure\nArguments: {"repoName": "BerriAI/litellm"}'}],model:"mcp-tool-call"},[P,L]=(0,r.useState)(JSON.stringify(A,null,2)),[B,F]=(0,r.useState)(null),[$,E]=(0,r.useState)(null),M=(0,r.useRef)(null),R=e=>null==e?["pre_call"]:Array.isArray(e)?e.length?e:["pre_call"]:[e];(0,r.useEffect)(()=>{e&&(s?(d(s.guardrail_name||""),p(R(s.litellm_params?.mode)),f(s.litellm_params?.default_on||!1),v(s.litellm_params?.custom_code||tY.empty.code),_("")):(d(""),p(["pre_call"]),f(!1),_("empty"),v(tY.empty.code)),F(null),I(!1))},[e,s]);let z=async e=>{try{await navigator.clipboard.writeText(e),E(e),setTimeout(()=>E(null),2e3)}catch(e){console.error("Failed to copy:",e)}},D=async()=>{if(!o.trim())return void y.default.fromBackend("Please enter a guardrail name");if(!b.trim())return void y.default.fromBackend("Please enter custom code");if(!i)return void y.default.fromBackend("No access token available");C(!0);try{if(n&&s){let e={litellm_params:{custom_code:b}};o!==s.guardrail_name&&(e.guardrail_name=o);let t=R(s.litellm_params?.mode);(u.length!==t.length||u.some((e,a)=>e!==t[a]))&&(e.litellm_params.mode=u),h!==s.litellm_params?.default_on&&(e.litellm_params.default_on=h),await (0,m.updateGuardrailCall)(i,s.guardrail_id,e),y.default.success("Custom code guardrail updated successfully")}else await (0,m.createGuardrailCall)(i,{guardrail_name:o,litellm_params:{guardrail:"custom_code",mode:u,default_on:h,custom_code:b},guardrail_info:{}}),y.default.success("Custom code guardrail created successfully");a(),t()}catch(e){console.error("Failed to save guardrail:",e),y.default.fromBackend(`Failed to ${n?"update":"create"} guardrail: `+(e instanceof Error?e.message:String(e)))}finally{C(!1)}},K=async()=>{if(!i)return void F({error:"No access token available"});k(!0),F(null);try{let e;try{e=JSON.parse(P)}catch(e){F({error:"Invalid test input JSON"}),k(!1);return}e.texts||(e.texts=[]);let t=["pre_call","pre_mcp_call"],a=["post_call","post_mcp_call"],l=u.some(e=>t.includes(e))?"request":u.some(e=>a.includes(e))?"response":"request",r=await (0,m.testCustomCodeGuardrail)(i,{custom_code:b,test_input:e,input_type:l,request_data:{model:"test-model",metadata:{}}});r.success&&r.result?F(r.result):r.error?F({error:r.error,error_type:r.error_type}):F({error:"Unknown error occurred"})}catch(e){console.error("Failed to test custom code:",e),F({error:e instanceof Error?e.message:"Failed to test custom code"})}finally{k(!1)}},q=b.split("\n").length;return(0,l.jsxs)(g.Modal,{open:e,onCancel:t,footer:null,width:1400,className:"custom-code-modal",closable:!0,destroyOnClose:!0,children:[(0,l.jsxs)("div",{className:"flex flex-col h-[80vh]",children:[(0,l.jsxs)("div",{className:"pb-4 border-b border-gray-200",children:[(0,l.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:n?"Edit Custom Guardrail":"Create Custom Guardrail"}),(0,l.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"Define custom logic using Python-like syntax"})]}),(0,l.jsxs)("div",{className:"flex items-center gap-4 py-4 border-b border-gray-100",children:[(0,l.jsxs)("div",{className:"flex-1 max-w-[200px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Guardrail Name"}),(0,l.jsx)(tu.TextInput,{value:o,onValueChange:d,placeholder:"e.g., block-pii-custom"})]}),(0,l.jsxs)("div",{className:"w-[280px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Mode (can select multiple)"}),(0,l.jsx)(x.Select,{mode:"multiple",value:u,onChange:p,options:tX,className:"w-full",size:"middle",placeholder:"Select modes"})]}),(0,l.jsxs)("div",{className:"w-[180px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Template"}),(0,l.jsx)(x.Select,{value:j,onChange:e=>{_(e),v(tY[e].code)},className:"w-full",size:"middle",dropdownRender:e=>(0,l.jsxs)(l.Fragment,{children:[e,(0,l.jsx)(eJ.Divider,{style:{margin:"8px 0"}}),(0,l.jsxs)("div",{style:{padding:"8px 12px",cursor:"pointer",color:"#1890ff",fontSize:"12px",display:"flex",alignItems:"center",gap:"4px"},onClick:e=>{e.preventDefault(),window.open("https://models.litellm.ai/guardrails","_blank")},onMouseEnter:e=>{e.currentTarget.style.backgroundColor="#f0f0f0"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent"},children:[(0,l.jsx)(tU,{}),(0,l.jsx)("span",{children:"Browse Community templates"}),(0,l.jsx)(tJ.ExportOutlined,{style:{fontSize:"10px"}})]})]}),children:(0,l.jsx)(x.Select.OptGroup,{label:"STANDARD",children:Object.entries(tY).map(([e,t])=>(0,l.jsx)(x.Select.Option,{value:e,children:t.name},e))})})]}),(0,l.jsxs)("div",{className:"flex items-center gap-2 pt-5",children:[(0,l.jsx)("span",{className:"text-sm text-gray-600",children:"Default On"}),(0,l.jsx)(U.Switch,{checked:h,onChange:f})]})]}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-hidden mt-4 gap-6",children:[(0,l.jsxs)("div",{className:"flex-2 flex flex-col min-w-0 overflow-y-auto",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2 shrink-0",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wide",children:"Python Logic"}),(0,l.jsx)("span",{className:"text-xs text-gray-400",children:"Restricted environment (no imports)"})]}),(0,l.jsxs)("div",{className:"relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e] shrink-0",style:{minHeight:"300px",maxHeight:"400px"},children:[(0,l.jsx)("div",{className:"absolute left-0 top-0 bottom-0 w-12 bg-[#1e1e1e] border-r border-gray-700 text-right pr-3 pt-3 select-none overflow-hidden",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace",fontSize:"14px",lineHeight:"1.6"},children:Array.from({length:Math.max(q,20)},(e,t)=>(0,l.jsx)("div",{className:"text-gray-500 h-[22.4px]",children:t+1},t+1))}),(0,l.jsx)("textarea",{ref:M,value:b,onChange:e=>v(e.target.value),onKeyDown:e=>{if("Tab"===e.key){e.preventDefault();let t=e.currentTarget,a=t.selectionStart,l=t.selectionEnd;v(b.substring(0,a)+" "+b.substring(l)),setTimeout(()=>{t.selectionStart=t.selectionEnd=a+4},0)}},spellCheck:!1,className:"w-full h-full pl-14 pr-4 pt-3 pb-3 resize-none focus:outline-hidden bg-transparent text-gray-200",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace",fontSize:"14px",lineHeight:"1.6",tabSize:4}})]}),(0,l.jsx)(G.Collapse,{activeKey:S?["test"]:[],onChange:e=>I(e.includes("test")),className:"mt-3 bg-white border border-gray-200 rounded-lg shrink-0",expandIcon:({isActive:e})=>(0,l.jsx)(tK,{rotate:90*!!e}),children:(0,l.jsx)(tW,{header:(0,l.jsxs)("span",{className:"flex items-center gap-2 text-sm font-medium",children:[(0,l.jsx)(tR.PlayCircleOutlined,{className:"text-blue-500"}),"Test Your Guardrail"]}),children:(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600",children:"Test Input (JSON)"}),(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"text-xs text-gray-500",children:"Load example:"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(A,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100 transition-colors",children:"Pre-call"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(T,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 transition-colors",children:"Pre MCP"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(O,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-green-200 bg-green-50 text-green-700 hover:bg-green-100 transition-colors",children:"Post-call"})]})]}),(0,l.jsx)("div",{className:"mb-2 p-2 bg-gray-50 rounded-sm text-xs text-gray-600 border border-gray-200",children:(0,l.jsxs)("div",{className:"grid grid-cols-2 gap-x-4 gap-y-1",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"texts"}),": Message content (always)"]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"images"}),": Base64 images (vision)"]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"tools"}),": Tool definitions ",(0,l.jsx)("span",{className:"text-orange-600",children:"(pre_call)"}),", MCP as OpenAI tool ",(0,l.jsx)("span",{className:"text-purple-600",children:"(pre_mcp_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"tool_calls"}),": LLM tool calls"," ",(0,l.jsx)("span",{className:"text-green-600",children:"(post_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"structured_messages"}),": Full messages"," ",(0,l.jsx)("span",{className:"text-orange-600",children:"(pre_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"model"}),": Model name (always)"]})]})}),(0,l.jsx)(tV,{value:P,onChange:e=>L(e.target.value),rows:8,className:"font-mono text-xs",placeholder:'{"texts": ["test message"], ...}'})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)(tm.Button,{size:"xs",onClick:K,disabled:N,icon:tR.PlayCircleOutlined,children:N?"Running...":"Run Test"}),B&&(0,l.jsx)("div",{className:`flex items-center gap-2 text-sm ${B.error?"text-red-600":"allow"===B.action?"text-green-600":"block"===B.action?"text-orange-600":"text-blue-600"}`,children:B.error?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tz.CloseCircleOutlined,{}),(0,l.jsxs)("span",{children:[B.error_type&&(0,l.jsxs)("span",{className:"font-medium",children:["[",B.error_type,"] "]}),B.error]})]}):"allow"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," Allowed"]}):"block"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tz.CloseCircleOutlined,{})," Blocked: ",B.reason]}):"modify"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," Modified",B.texts&&B.texts.length>0&&(0,l.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:["→ ",B.texts[0].substring(0,50),B.texts[0].length>50?"...":""]})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," ",B.action||"Unknown"]})})]})]})},"test")}),(0,l.jsxs)("div",{className:"mt-3 p-4 bg-linear-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-lg flex items-center justify-between shrink-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)("div",{className:"bg-blue-100 rounded-full p-2",children:(0,l.jsx)(tU,{className:"text-blue-600 text-lg"})}),(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"text-sm font-medium text-gray-900",children:"Built a useful guardrail?"}),(0,l.jsx)("div",{className:"text-xs text-gray-600",children:"Share it with the community and help others build faster"})]})]}),(0,l.jsx)(tm.Button,{size:"xs",onClick:()=>window.open("https://github.com/BerriAI/litellm-guardrails","_blank"),icon:tJ.ExportOutlined,className:"bg-blue-600 hover:bg-blue-700 text-white border-0",children:"Contribute Template"})]})]}),(0,l.jsxs)("div",{className:"w-[300px] shrink-0 overflow-auto border-l border-gray-200 pl-6",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-3",children:[(0,l.jsx)(c.CodeOutlined,{className:"text-blue-500"}),(0,l.jsx)("span",{className:"font-semibold text-gray-700",children:"Available Primitives"})]}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Click to copy functions to clipboard"}),(0,l.jsx)(G.Collapse,{defaultActiveKey:["Return Values"],className:"primitives-collapse bg-transparent border-0",expandIconPosition:"end",children:Object.entries(tQ).map(([e,t])=>(0,l.jsx)(tW,{header:(0,l.jsx)("span",{className:"text-sm font-medium text-gray-700",children:e}),className:"bg-white mb-2 rounded-lg border border-gray-200",children:(0,l.jsx)("div",{className:"space-y-2",children:t.map(e=>(0,l.jsx)("button",{onClick:()=>z(e.name),className:`w-full text-left px-2 py-2 rounded transition-colors ${$===e.name?"bg-green-100":"bg-gray-50 hover:bg-blue-50"}`,children:$===e.name?(0,l.jsxs)("span",{className:"flex items-center gap-1 text-xs font-mono text-green-700",children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," Copied!"]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"text-xs font-mono text-gray-800",children:e.name}),(0,l.jsx)("div",{className:"text-[10px] text-gray-500 mt-0.5",children:e.desc})]})},e.name))})},e))})]})]}),(0,l.jsxs)("div",{className:"flex items-center justify-between pt-4 mt-4 border-t border-gray-200",children:[(0,l.jsx)("span",{className:"text-xs text-gray-400",children:"Changes are auto-saved to local draft"}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)(tm.Button,{variant:"secondary",onClick:t,children:"Cancel"}),(0,l.jsx)(tm.Button,{onClick:D,loading:w,disabled:w||!o.trim(),icon:tq.SaveOutlined,children:n?"Update Guardrail":"Save Guardrail"})]})]})]}),(0,l.jsx)("style",{children:` .custom-code-modal .ant-modal-content { padding: 24px; } @@ -83,4 +83,4 @@ .primitives-collapse .ant-collapse-content-box { padding: 8px 12px !important; } - `})]})},tW=({guardrailId:e,onClose:t,accessToken:a,isAdmin:s})=>{let n,[o,d]=(0,r.useState)(null),[g,h]=(0,r.useState)(null),[f,j]=(0,r.useState)(!0),[_,b]=(0,r.useState)(!1),[v]=u.Form.useForm(),[w,N]=(0,r.useState)([]),[C,S]=(0,r.useState)({}),[k,I]=(0,r.useState)(null),[A,O]=(0,r.useState)({}),[T,P]=(0,r.useState)(!1),L={rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""},[B,F]=(0,r.useState)(L),[$,E]=(0,r.useState)(!1),[M,R]=(0,r.useState)(!1),G=r.default.useRef({patterns:[],blockedWords:[],categories:[]}),z=(0,r.useCallback)((e,t,a,l,r)=>{G.current={patterns:e,blockedWords:t,categories:a||[],competitorIntentEnabled:l,competitorIntentConfig:r}},[]),D=async()=>{try{if(j(!0),!a)return;let t=await (0,m.getGuardrailInfo)(a,e);if(d(t),t.litellm_params?.pii_entities_config){let e=t.litellm_params.pii_entities_config;if(N([]),S({}),Object.keys(e).length>0){let t=[],a={};Object.entries(e).forEach(([e,l])=>{t.push(e),a[e]="string"==typeof l?l:"MASK"}),N(t),S(a)}}else N([]),S({})}catch(e){y.default.fromBackend("Failed to load guardrail information"),console.error("Error fetching guardrail info:",e)}finally{j(!1)}},K=async()=>{try{if(!a)return;let e=await (0,m.getGuardrailProviderSpecificParams)(a);h(e)}catch(e){console.error("Error fetching guardrail provider specific params:",e)}},q=async()=>{try{if(!a)return;let e=await (0,m.getGuardrailUISettings)(a);I(e)}catch(e){console.error("Error fetching guardrail UI settings:",e)}};(0,r.useEffect)(()=>{K()},[a]),(0,r.useEffect)(()=>{D(),q()},[e,a]),(0,r.useEffect)(()=>{if(o&&v){let e={...o.litellm_params||{}};delete e.skip_system_message_in_guardrail,delete e.skip_tool_message_in_guardrail,v.setFieldsValue({guardrail_name:o.guardrail_name,...e,skip_system_message_choice:ex(o.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:eh(o.litellm_params?.skip_tool_message_in_guardrail),guardrail_info:o.guardrail_info?JSON.stringify(o.guardrail_info,null,2):"",...o.litellm_params?.optional_params&&{optional_params:o.litellm_params.optional_params}})}},[o,g,v]);let H=(0,r.useCallback)(()=>{o?.litellm_params?.guardrail==="tool_permission"?F({rules:o.litellm_params?.rules||[],default_action:(o.litellm_params?.default_action||"deny").toLowerCase(),on_disallowed_action:(o.litellm_params?.on_disallowed_action||"block").toLowerCase(),violation_message_template:o.litellm_params?.violation_message_template||""}):F(L),E(!1)},[o]);(0,r.useEffect)(()=>{H()},[H]);let U=async t=>{try{if(!a)return;let d={litellm_params:{}};t.guardrail_name!==o.guardrail_name&&(d.guardrail_name=t.guardrail_name),t.default_on!==o.litellm_params?.default_on&&(d.litellm_params.default_on=t.default_on);let c=ex(o.litellm_params?.skip_system_message_in_guardrail),u=t.skip_system_message_choice;void 0!==u&&u!==c&&("inherit"===u?d.litellm_params.skip_system_message_in_guardrail=null:"yes"===u?d.litellm_params.skip_system_message_in_guardrail=!0:d.litellm_params.skip_system_message_in_guardrail=!1);let p=eh(o.litellm_params?.skip_tool_message_in_guardrail),x=t.skip_tool_message_choice;void 0!==x&&x!==p&&("inherit"===x?d.litellm_params.skip_tool_message_in_guardrail=null:"yes"===x?d.litellm_params.skip_tool_message_in_guardrail=!0:d.litellm_params.skip_tool_message_in_guardrail=!1);let h=o.guardrail_info,f=t.guardrail_info?JSON.parse(t.guardrail_info):void 0;JSON.stringify(h)!==JSON.stringify(f)&&(d.guardrail_info=f);let j=o.litellm_params?.pii_entities_config||{},_={};if(w.forEach(e=>{_[e]=C[e]||"MASK"}),JSON.stringify(j)!==JSON.stringify(_)&&(d.litellm_params.pii_entities_config=_),o.litellm_params?.guardrail==="litellm_content_filter"&&T){var l,r,i,s,n;let e,t=(l=G.current.patterns||[],r=G.current.blockedWords||[],i=G.current.categories||[],s=G.current.competitorIntentEnabled,n=G.current.competitorIntentConfig,e={patterns:l.map(e=>({pattern_type:"prebuilt"===e.type?"prebuilt":"regex",pattern_name:"prebuilt"===e.type?e.name:void 0,pattern:"custom"===e.type?e.pattern:void 0,name:e.name,action:e.action})),blocked_words:r.map(e=>({keyword:e.keyword,action:e.action,description:e.description}))},void 0!==i&&(e.categories=i.map(e=>({category:e.category,enabled:!0,action:e.action,severity_threshold:e.severity_threshold||"medium"}))),s&&n&&n.brand_self.length>0&&(e.competitor_intent_config={competitor_intent_type:n.competitor_intent_type,brand_self:n.brand_self,locations:n.locations?.length?n.locations:void 0,competitors:"generic"===n.competitor_intent_type&&n.competitors?.length?n.competitors:void 0,policy:n.policy,threshold_high:n.threshold_high,threshold_medium:n.threshold_medium,threshold_low:n.threshold_low}),e);d.litellm_params.patterns=t.patterns,d.litellm_params.blocked_words=t.blocked_words,d.litellm_params.categories=t.categories,d.litellm_params.competitor_intent_config=t.competitor_intent_config??null}if(o.litellm_params?.guardrail==="tool_permission"){let e=o.litellm_params?.rules||[],t=B.rules||[],a=JSON.stringify(e)!==JSON.stringify(t),l=(o.litellm_params?.default_action||"deny").toLowerCase(),r=(B.default_action||"deny").toLowerCase(),i=l!==r,s=(o.litellm_params?.on_disallowed_action||"block").toLowerCase(),n=(B.on_disallowed_action||"block").toLowerCase(),c=s!==n,m=o.litellm_params?.violation_message_template||"",u=B.violation_message_template||"",p=m!==u;($||a||i||c||p)&&(d.litellm_params.rules=t,d.litellm_params.default_action=r,d.litellm_params.on_disallowed_action=n,d.litellm_params.violation_message_template=u||null)}let v=Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail),N=o.litellm_params?.guardrail==="tool_permission";if(g&&v&&!N){let e=g[en[v]?.toLowerCase()]||{},a=new Set;Object.keys(e).forEach(e=>{"optional_params"!==e&&a.add(e)}),e.optional_params&&e.optional_params.fields&&Object.keys(e.optional_params.fields).forEach(e=>{a.add(e)}),a.forEach(e=>{if("patterns"===e||"blocked_words"===e||"categories"===e)return;let a=t[e];(null==a||""===a)&&(a=t.optional_params?.[e]);let l=o.litellm_params?.[e];JSON.stringify(a)!==JSON.stringify(l)&&(null!=a&&""!==a?d.litellm_params[e]=a:null!=l&&""!==l&&(d.litellm_params[e]=null))})}if(0===Object.keys(d.litellm_params).length&&delete d.litellm_params,0===Object.keys(d).length){y.default.info("No changes detected"),b(!1);return}await (0,m.updateGuardrailCall)(a,e,d),y.default.success("Guardrail updated successfully"),P(!1),D(),b(!1)}catch(e){console.error("Error updating guardrail:",e),y.default.fromBackend("Failed to update guardrail")}};if(f)return(0,l.jsx)("div",{className:"p-4",children:"Loading..."});if(!o)return(0,l.jsx)("div",{className:"p-4",children:"Guardrail not found"});let J=e=>e?new Date(e).toLocaleString():"-",{logo:W,displayName:V}=eg(o.litellm_params?.guardrail||""),Y=async(e,t)=>{await (0,tx.copyToClipboard)(e)&&(O(e=>({...e,[t]:!0})),setTimeout(()=>{O(e=>({...e,[t]:!1}))},2e3))},Q="config"===o.guardrail_definition_location;return(0,l.jsxs)("div",{className:"p-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(i.Button,{type:"text",icon:(0,l.jsx)(th.ArrowLeftIcon,{className:"w-4 h-4"}),onClick:t,className:"mb-4",children:"Back to Guardrails"}),(0,l.jsx)(tw.Title,{children:o.guardrail_name||"Unnamed Guardrail"}),(0,l.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,l.jsx)(eq.Text,{className:"text-gray-500 font-mono",children:o.guardrail_id}),(0,l.jsx)(i.Button,{type:"text",size:"small",icon:A["guardrail-id"]?(0,l.jsx)(tN.CheckIcon,{size:12}):(0,l.jsx)(tC.CopyIcon,{size:12}),onClick:()=>Y(o.guardrail_id,"guardrail-id"),className:`left-2 z-10 transition-all duration-200 ${A["guardrail-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]}),(0,l.jsxs)(tj.TabGroup,{children:[(0,l.jsxs)(t_.TabList,{className:"mb-4",children:[(0,l.jsx)(ty.Tab,{children:"Overview"},"overview"),s?(0,l.jsx)(ty.Tab,{children:"Settings"},"settings"):(0,l.jsx)(l.Fragment,{})]}),(0,l.jsxs)(tv.TabPanels,{children:[(0,l.jsxs)(tb.TabPanel,{children:[(0,l.jsxs)(tf.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,l.jsxs)(eK.Card,{children:[(0,l.jsx)(eq.Text,{children:"Provider"}),(0,l.jsxs)("div",{className:"mt-2 flex items-center space-x-2",children:[W&&(0,l.jsx)("img",{src:W,alt:`${V} logo`,className:"w-6 h-6",onError:e=>{e.target.style.display="none"}}),(0,l.jsx)(tw.Title,{children:V})]})]}),(0,l.jsxs)(eK.Card,{children:[(0,l.jsx)(eq.Text,{children:"Mode"}),(0,l.jsxs)("div",{className:"mt-2",children:[(0,l.jsx)(tw.Title,{children:o.litellm_params?.mode||"-"}),(0,l.jsx)(tr.Badge,{color:o.litellm_params?.default_on?"green":"gray",children:o.litellm_params?.default_on?"Default On":"Default Off"})]})]}),(0,l.jsxs)(eK.Card,{children:[(0,l.jsx)(eq.Text,{children:"Created At"}),(0,l.jsxs)("div",{className:"mt-2",children:[(0,l.jsx)(tw.Title,{children:J(o.created_at)}),(0,l.jsxs)(eq.Text,{children:["Last Updated: ",J(o.updated_at)]})]})]})]}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsx)(eK.Card,{className:"mt-6",children:(0,l.jsxs)("div",{className:"flex justify-between items-center",children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"PII Protection"}),(0,l.jsxs)(tr.Badge,{color:"blue",children:[Object.keys(o.litellm_params.pii_entities_config).length," PII entities configured"]})]})}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsxs)(eK.Card,{className:"mt-6",children:[(0,l.jsx)(eq.Text,{className:"mb-4 text-lg font-semibold",children:"PII Entity Configuration"}),(0,l.jsxs)("div",{className:"border rounded-lg overflow-hidden shadow-xs",children:[(0,l.jsxs)("div",{className:"bg-gray-50 px-5 py-3 border-b flex",children:[(0,l.jsx)(eq.Text,{className:"flex-1 font-semibold text-gray-700",children:"Entity Type"}),(0,l.jsx)(eq.Text,{className:"flex-1 font-semibold text-gray-700",children:"Configuration"})]}),(0,l.jsx)("div",{className:"max-h-[400px] overflow-y-auto",children:Object.entries(o.litellm_params?.pii_entities_config).map(([e,t])=>(0,l.jsxs)("div",{className:"px-5 py-3 flex border-b hover:bg-gray-50 transition-colors",children:[(0,l.jsx)(eq.Text,{className:"flex-1 font-medium text-gray-900",children:e}),(0,l.jsx)(eq.Text,{className:"flex-1",children:(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 ${"MASK"===t?"text-blue-600":"text-red-600"}`,children:["MASK"===t?(0,l.jsx)(eA.default,{}):(0,l.jsx)(eO.StopOutlined,{}),String(t)]})})]},e))})]})]}),o.litellm_params?.guardrail==="tool_permission"&&(0,l.jsx)(eK.Card,{className:"mt-6",children:(0,l.jsx)(eV,{value:B,disabled:!0})}),o.litellm_params?.guardrail==="custom_code"&&o.litellm_params?.custom_code&&(0,l.jsxs)(eK.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)(c.CodeOutlined,{className:"text-blue-500"}),(0,l.jsx)(eq.Text,{className:"font-medium text-lg",children:"Custom Code"})]}),s&&!Q&&(0,l.jsx)(i.Button,{size:"small",icon:(0,l.jsx)(c.CodeOutlined,{}),onClick:()=>R(!0),children:"Edit Code"})]}),(0,l.jsx)("div",{className:"relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e]",children:(0,l.jsx)("pre",{className:"p-4 text-sm text-gray-200 overflow-x-auto",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace"},children:(0,l.jsx)("code",{children:o.litellm_params.custom_code})})})]}),(0,l.jsx)(tP,{guardrailData:o,guardrailSettings:k,isEditing:!1,accessToken:a})]}),s&&(0,l.jsx)(tb.TabPanel,{children:(0,l.jsxs)(eK.Card,{children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(tw.Title,{children:"Guardrail Settings"}),Q&&(0,l.jsx)(eN.Tooltip,{title:"Guardrail is defined in the config file and cannot be edited.",children:(0,l.jsx)(eJ.InfoCircleOutlined,{})}),!_&&!Q&&(o.litellm_params?.guardrail==="custom_code"?(0,l.jsx)(i.Button,{icon:(0,l.jsx)(c.CodeOutlined,{}),onClick:()=>R(!0),children:"Edit Code"}):(0,l.jsx)(i.Button,{onClick:()=>b(!0),children:"Edit Settings"}))]}),_?(0,l.jsxs)(u.Form,{form:v,onFinish:U,initialValues:{guardrail_name:o.guardrail_name,...(n={...o.litellm_params||{}},delete n.skip_system_message_in_guardrail,delete n.skip_tool_message_in_guardrail,n),skip_system_message_choice:ex(o.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:eh(o.litellm_params?.skip_tool_message_in_guardrail),guardrail_info:o.guardrail_info?JSON.stringify(o.guardrail_info,null,2):"",...o.litellm_params?.optional_params&&{optional_params:o.litellm_params.optional_params}},layout:"vertical",children:[(0,l.jsx)(u.Form.Item,{label:"Guardrail Name",name:"guardrail_name",rules:[{required:!0,message:"Please input a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"Enter guardrail name"})}),(0,l.jsx)(u.Form.Item,{label:"Default On",name:"default_on",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"Yes"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"No"})]})}),(0,l.jsx)(u.Form.Item,{label:"Skip system messages in guardrail",name:"skip_system_message_choice",tooltip:"Unified guardrails: omit role: system from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{label:"Skip tool messages in guardrail",name:"skip_tool_message_choice",tooltip:"Unified guardrails: omit role: tool from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),o.litellm_params?.guardrail==="presidio"&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eH.Divider,{orientation:"left",children:"PII Protection"}),(0,l.jsx)("div",{className:"mb-6",children:k&&(0,l.jsx)(eD,{entities:k.supported_entities,actions:k.supported_actions,selectedEntities:w,selectedActions:C,onEntitySelect:e=>{N(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},onActionSelect:(e,t)=>{S(a=>({...a,[e]:t}))},entityCategories:k.pii_entity_categories})})]}),(0,l.jsx)(tP,{guardrailData:o,guardrailSettings:k,isEditing:!0,accessToken:a,onDataChange:z,onUnsavedChanges:P}),(o.litellm_params?.guardrail==="tool_permission"||g)&&(0,l.jsx)(eH.Divider,{orientation:"left",children:"Provider Settings"}),o.litellm_params?.guardrail==="tool_permission"?(0,l.jsx)(eV,{value:B,onChange:F}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(ew,{selectedProvider:Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail)||null,accessToken:a,providerParams:g,value:o.litellm_params}),g&&(()=>{let e=Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail);if(!e)return null;let t=g[en[e]?.toLowerCase()];return t&&t.optional_params?(0,l.jsx)(e_,{optionalParams:t.optional_params,parentFieldKey:"optional_params",values:o.litellm_params}):null})()]}),(0,l.jsx)(eH.Divider,{orientation:"left",children:"Advanced Settings"}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Information",name:"guardrail_info",children:(0,l.jsx)(p.Input.TextArea,{rows:5})}),(0,l.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,l.jsx)(i.Button,{onClick:()=>{b(!1),P(!1),H()},children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",htmlType:"submit",children:"Save Changes"})]})]}):(0,l.jsxs)("div",{className:"space-y-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Guardrail ID"}),(0,l.jsx)("div",{className:"font-mono",children:o.guardrail_id})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Guardrail Name"}),(0,l.jsx)("div",{children:o.guardrail_name||"Unnamed Guardrail"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Provider"}),(0,l.jsx)("div",{children:V})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Mode"}),(0,l.jsx)("div",{children:o.litellm_params?.mode||"-"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Default On"}),(0,l.jsx)(tr.Badge,{color:o.litellm_params?.default_on?"green":"gray",children:o.litellm_params?.default_on?"Yes":"No"})]}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"PII Protection"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsxs)(tr.Badge,{color:"blue",children:[Object.keys(o.litellm_params.pii_entities_config).length," PII entities configured"]})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Created At"}),(0,l.jsx)("div",{children:J(o.created_at)})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eq.Text,{className:"font-medium",children:"Last Updated"}),(0,l.jsx)("div",{children:J(o.updated_at)})]}),o.litellm_params?.guardrail==="tool_permission"&&(0,l.jsx)(eV,{value:B,disabled:!0})]})]})})]})]}),(0,l.jsx)(tJ,{visible:M,onClose:()=>R(!1),onSuccess:()=>{R(!1),D()},accessToken:a,editData:o?{guardrail_id:o.guardrail_id,guardrail_name:o.guardrail_name,litellm_params:o.litellm_params}:null})]})};var tV=e.i(573421),tY=e.i(19732),tQ=e.i(928685),tX=e.i(166406),tZ=e.i(637235),t0=e.i(240647);let{Text:t1}=f.Typography,t2=function({results:e,errors:t}){let[a,i]=(0,r.useState)(new Set),s=e=>{let t=new Set(a);t.has(e)?t.delete(e):t.add(e),i(t)},n=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0;{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.focus(),t.select();let a=document.execCommand("copy");if(document.body.removeChild(t),!a)throw Error("execCommand failed");return!0}}catch(e){return console.error("Copy failed:",e),!1}};return e||t?(0,l.jsxs)("div",{className:"space-y-3 pt-4 border-t border-gray-200",children:[(0,l.jsx)("h3",{className:"text-sm font-semibold text-gray-900",children:"Results"}),e&&e.map(e=>{let t=a.has(e.guardrailName);return(0,l.jsx)(eK.Card,{className:"bg-green-50 border-green-200",children:(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-2 cursor-pointer flex-1",onClick:()=>s(e.guardrailName),children:[t?(0,l.jsx)(t0.RightOutlined,{className:"text-gray-500 text-xs"}):(0,l.jsx)(o.DownOutlined,{className:"text-gray-500 text-xs"}),(0,l.jsx)(tB.CheckCircleOutlined,{className:"text-green-600 text-lg"}),(0,l.jsx)("span",{className:"text-sm font-medium text-green-800",children:e.guardrailName})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-600",children:[(0,l.jsx)(tZ.ClockCircleOutlined,{}),(0,l.jsxs)("span",{className:"font-medium",children:[e.latency,"ms"]})]}),!t&&(0,l.jsx)(e9.Button,{size:"xs",variant:"secondary",icon:tX.CopyOutlined,onClick:async()=>{await n(e.response_text)?y.default.success("Result copied to clipboard"):y.default.fromBackend("Failed to copy result")},children:"Copy"})]})]}),!t&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"bg-white border border-green-200 rounded-sm p-3",children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-600 mb-2 block",children:"Output Text"}),(0,l.jsx)("div",{className:"font-mono text-sm text-gray-900 whitespace-pre-wrap wrap-break-word",children:e.response_text})]}),(0,l.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,l.jsx)("span",{className:"font-medium",children:"Characters:"})," ",e.response_text.length]})]})]})},e.guardrailName)}),t&&t.map(e=>{let t=a.has(e.guardrailName);return(0,l.jsx)(eK.Card,{className:"bg-red-50 border-red-200",children:(0,l.jsxs)("div",{className:"flex items-start space-x-2",children:[(0,l.jsx)("div",{className:"cursor-pointer mt-0.5",onClick:()=>s(e.guardrailName),children:t?(0,l.jsx)(t0.RightOutlined,{className:"text-gray-500 text-xs"}):(0,l.jsx)(o.DownOutlined,{className:"text-gray-500 text-xs"})}),(0,l.jsx)("div",{className:"text-red-600 mt-0.5",children:(0,l.jsx)("svg",{className:"w-5 h-5",fill:"currentColor",viewBox:"0 0 20 20",children:(0,l.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",clipRule:"evenodd"})})}),(0,l.jsxs)("div",{className:"flex-1",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,l.jsxs)("p",{className:"text-sm font-medium text-red-800 cursor-pointer",onClick:()=>s(e.guardrailName),children:[e.guardrailName," - Error"]}),(0,l.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-600",children:[(0,l.jsx)(tZ.ClockCircleOutlined,{}),(0,l.jsxs)("span",{className:"font-medium",children:[e.latency,"ms"]})]})]}),!t&&(0,l.jsx)("p",{className:"text-sm text-red-700 mt-1",children:e.error.message})]})]})},e.guardrailName)})]}):null},{TextArea:t4}=p.Input,{Text:t5}=f.Typography,t8=function({guardrailNames:e,onSubmit:t,isLoading:a,results:i,errors:s,onClose:n}){let[o,d]=(0,r.useState)(""),c=()=>{o.trim()?t(o):y.default.fromBackend("Please enter text to test")},m=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0;{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.focus(),t.select();let a=document.execCommand("copy");if(document.body.removeChild(t),!a)throw Error("execCommand failed");return!0}}catch(e){return console.error("Copy failed:",e),!1}},u=async()=>{await m(o)?y.default.success("Input copied to clipboard"):y.default.fromBackend("Failed to copy input")};return(0,l.jsxs)("div",{className:"space-y-4 h-full flex flex-col",children:[(0,l.jsx)("div",{className:"flex items-center justify-between pb-3 border-b border-gray-200",children:(0,l.jsx)("div",{className:"flex items-center space-x-3",children:(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-2 mb-1",children:[(0,l.jsx)("h2",{className:"text-lg font-semibold text-gray-900",children:"Test Guardrails:"}),(0,l.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map(e=>(0,l.jsx)("div",{className:"inline-flex items-center space-x-1 bg-blue-50 px-3 py-1 rounded-md border border-blue-200",children:(0,l.jsx)("span",{className:"font-mono text-blue-700 font-medium text-sm",children:e})},e))})]}),(0,l.jsxs)("p",{className:"text-sm text-gray-500",children:["Test ",e.length>1?"guardrails":"guardrail"," and compare results"]})]})})}),(0,l.jsxs)("div",{className:"flex-1 overflow-auto space-y-4",children:[(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-2",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Input Text"}),(0,l.jsx)(eN.Tooltip,{title:"Press Enter to submit. Use Shift+Enter for new line.",children:(0,l.jsx)(eJ.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),o&&(0,l.jsx)(e9.Button,{size:"xs",variant:"secondary",icon:tX.CopyOutlined,onClick:u,children:"Copy Input"})]}),(0,l.jsx)(t4,{value:o,onChange:e=>d(e.target.value),onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||e.ctrlKey||e.metaKey||(e.preventDefault(),c())},placeholder:"Enter text to test with guardrails...",rows:8,className:"font-mono text-sm"}),(0,l.jsxs)("div",{className:"flex justify-between items-center mt-1",children:[(0,l.jsxs)(t5,{className:"text-xs text-gray-500",children:["Press ",(0,l.jsx)("kbd",{className:"px-1 py-0.5 bg-gray-100 border border-gray-300 rounded-sm text-xs",children:"Enter"})," to submit •"," ",(0,l.jsx)("kbd",{className:"px-1 py-0.5 bg-gray-100 border border-gray-300 rounded-sm text-xs",children:"Shift+Enter"})," for new line"]}),(0,l.jsxs)(t5,{className:"text-xs text-gray-500",children:["Characters: ",o.length]})]})]}),(0,l.jsx)("div",{className:"pt-2",children:(0,l.jsx)(e9.Button,{onClick:c,loading:a,disabled:!o.trim(),className:"w-full",children:a?`Testing ${e.length} guardrail${e.length>1?"s":""}...`:`Test ${e.length} guardrail${e.length>1?"s":""}`})})]}),(0,l.jsx)(t2,{results:i,errors:s})]})]})},t6=({guardrailsList:e,isLoading:t,accessToken:a,onClose:i})=>{let[s,n]=(0,r.useState)(new Set),[o,d]=(0,r.useState)(""),[c,u]=(0,r.useState)([]),[g,x]=(0,r.useState)([]),[h,j]=(0,r.useState)(!1),_=e.filter(e=>e.guardrail_name?.toLowerCase().includes(o.toLowerCase())),v=async e=>{if(0===s.size||!a)return;j(!0),u([]),x([]);let t=[],l=[];await Promise.all(Array.from(s).map(async r=>{let i=Date.now();try{let l=await (0,m.applyGuardrail)(a,r,e,null,null),s=Date.now()-i;t.push({guardrailName:r,response_text:l.response_text,latency:s})}catch(t){let e=Date.now()-i;console.error(`Error testing guardrail ${r}:`,t),l.push({guardrailName:r,error:t,latency:e})}})),u(t),x(l),j(!1),t.length>0&&y.default.success(`${t.length} guardrail${t.length>1?"s":""} applied successfully`),l.length>0&&y.default.fromBackend(`${l.length} guardrail${l.length>1?"s":""} failed`)};return(0,l.jsx)("div",{className:"w-full h-[calc(100vh-200px)]",children:(0,l.jsx)(b.Card,{className:"h-full",styles:{body:{padding:0,height:"100%"}},children:(0,l.jsxs)("div",{className:"flex h-full",children:[(0,l.jsxs)("div",{className:"w-1/4 border-r border-gray-200 flex flex-col overflow-hidden",children:[(0,l.jsx)("div",{className:"p-4 border-b border-gray-200",children:(0,l.jsxs)("div",{className:"mb-3",children:[(0,l.jsx)("h3",{className:"text-lg font-semibold mb-3",children:"Guardrails"}),(0,l.jsx)(p.Input,{prefix:(0,l.jsx)(tQ.SearchOutlined,{}),placeholder:"Search guardrails...",value:o,onChange:e=>d(e.target.value)})]})}),(0,l.jsx)("div",{className:"flex-1 overflow-auto",children:t?(0,l.jsx)("div",{className:"flex items-center justify-center h-32",children:(0,l.jsx)(eb.Spin,{})}):0===_.length?(0,l.jsx)("div",{className:"p-4",children:(0,l.jsx)(eU.Empty,{description:o?"No guardrails match your search":"No guardrails available"})}):(0,l.jsx)(tV.List,{dataSource:_,renderItem:e=>(0,l.jsx)(tV.List.Item,{onClick:()=>{var t;let a;e.guardrail_name&&(t=e.guardrail_name,(a=new Set(s)).has(t)?a.delete(t):a.add(t),n(a))},style:{paddingLeft:24,paddingRight:16},className:`cursor-pointer hover:bg-gray-50 transition-colors ${s.has(e.guardrail_name||"")?"bg-blue-50 border-l-4 border-l-blue-500":"border-l-4 border-l-transparent"}`,children:(0,l.jsx)(tV.List.Item.Meta,{title:(0,l.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,l.jsx)(tY.ExperimentOutlined,{className:"text-gray-400"}),(0,l.jsx)("span",{className:"font-medium text-gray-900",children:e.guardrail_name})]}),description:(0,l.jsxs)("div",{className:"text-xs space-y-1 mt-1",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-medium",children:"Type: "}),(0,l.jsx)("span",{className:"text-gray-600",children:e.litellm_params.guardrail})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-medium",children:"Mode: "}),(0,l.jsx)("span",{className:"text-gray-600",children:e.litellm_params.mode})]})]})})})})}),(0,l.jsx)("div",{className:"p-3 border-t border-gray-200 bg-gray-50",children:(0,l.jsxs)(f.Typography.Text,{className:"text-xs text-gray-600",children:[s.size," of ",_.length," selected"]})})]}),(0,l.jsxs)("div",{className:"w-3/4 flex flex-col bg-white",children:[(0,l.jsx)("div",{className:"p-4 border-b border-gray-200 flex justify-between items-center",children:(0,l.jsx)(f.Typography.Title,{level:2,className:"text-xl font-semibold mb-0",children:"Guardrail Testing Playground"})}),(0,l.jsx)("div",{className:"flex-1 overflow-auto p-4",children:0===s.size?(0,l.jsxs)("div",{className:"h-full flex flex-col items-center justify-center text-gray-400",children:[(0,l.jsx)(tY.ExperimentOutlined,{style:{fontSize:"48px",marginBottom:"16px"}}),(0,l.jsx)(f.Typography.Paragraph,{className:"text-lg font-medium text-gray-600 mb-2",children:"Select Guardrails to Test"}),(0,l.jsx)(f.Typography.Paragraph,{className:"text-center text-gray-500 max-w-md",children:"Choose one or more guardrails from the left sidebar to start testing and comparing results."})]}):(0,l.jsx)("div",{className:"h-full",children:(0,l.jsx)(t8,{guardrailNames:Array.from(s),onSubmit:v,results:c.length>0?c:null,errors:g.length>0?g:null,isLoading:h,onClose:()=>n(new Set)})})})]})]})})})};var t3=e.i(127952),t7=e.i(266537);let t9="/ui/assets/logos/",ae=[{id:"cf_denied_financial",name:"Denied Financial Advice",description:"Detects requests for personalized financial advice, investment recommendations, or financial planning.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"],eval:{f1:100,precision:100,recall:100,testCases:207,latency:"<0.1ms"}},{id:"cf_denied_insults",name:"Insults & Personal Attacks",description:"Detects insults, name-calling, and personal attacks directed at the chatbot, staff, or other people.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"],eval:{f1:100,precision:100,recall:100,testCases:299,latency:"<0.1ms"}},{id:"cf_denied_legal",name:"Denied Legal Advice",description:"Detects requests for unauthorized legal advice, case analysis, or legal recommendations.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"]},{id:"cf_denied_medical",name:"Denied Medical Advice",description:"Detects requests for medical diagnosis, treatment recommendations, or health advice.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"]},{id:"cf_harmful_violence",name:"Harmful Violence",description:"Detects content related to violence, criminal planning, attacks, and violent threats.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_self_harm",name:"Harmful Self-Harm",description:"Detects content related to self-harm, suicide, and dangerous self-destructive behavior.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_child_safety",name:"Harmful Child Safety",description:"Detects content that could endanger child safety or exploit minors.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_illegal_weapons",name:"Harmful Illegal Weapons",description:"Detects content related to illegal weapons manufacturing, distribution, or acquisition.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_bias_gender",name:"Bias: Gender",description:"Detects gender-based discrimination, stereotypes, and biased language.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_racial",name:"Bias: Racial",description:"Detects racial discrimination, stereotypes, and racially biased content.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_religious",name:"Bias: Religious",description:"Detects religious discrimination, intolerance, and religiously biased content.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_sexual_orientation",name:"Bias: Sexual Orientation",description:"Detects discrimination based on sexual orientation and related biased content.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_prompt_injection_jailbreak",name:"Prompt Injection: Jailbreak",description:"Detects jailbreak attempts designed to bypass AI safety guidelines and restrictions.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_data_exfil",name:"Prompt Injection: Data Exfiltration",description:"Detects attempts to extract sensitive data through prompt manipulation.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_sql",name:"Prompt Injection: SQL",description:"Detects SQL injection attempts embedded in prompts.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_malicious_code",name:"Prompt Injection: Malicious Code",description:"Detects attempts to inject malicious code through prompts.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_system_prompt",name:"Prompt Injection: System Prompt",description:"Detects attempts to extract or override system prompts.",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_toxic_abuse",name:"Toxic & Abusive Language",description:"Detects toxic, abusive, and hateful language across multiple languages (EN, AU, DE, ES, FR).",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Toxicity"]},{id:"cf_patterns",name:"Pattern Matching",description:"Detect and block sensitive data patterns like SSNs, credit card numbers, API keys, and custom regex patterns.",category:"litellm",subcategory:"Patterns",logo:`${t9}litellm_logo.jpg`,tags:["PII","Regex","Data Protection"]},{id:"cf_keywords",name:"Keyword Blocking",description:"Block or mask content containing specific keywords or phrases. Upload custom word lists or add individual terms.",category:"litellm",subcategory:"Keywords",logo:`${t9}litellm_logo.jpg`,tags:["Keywords","Blocklist"]},{id:"block_code_execution",name:"Block Code Execution",description:"Detects markdown fenced code blocks in requests and responses. Block or mask executable code (e.g. Python, JavaScript, Bash) by language with configurable confidence.",category:"litellm",subcategory:"Code Safety",logo:`${t9}litellm_logo.jpg`,tags:["Code","Safety","Prompt Injection"]},{id:"cf_competitor_intent",name:"Competitor Name Blocking",description:"Block or reframe competitor comparison and ranking intent. Detect when users ask to compare or recommend competitors (airline or generic competitor lists).",category:"litellm",subcategory:"Content Category",logo:`${t9}litellm_logo.jpg`,tags:["Content Category","Competitor","Topic Blocker"]},{id:"presidio",name:"Presidio PII",description:"Microsoft Presidio for PII detection and anonymization. Supports 30+ entity types with configurable actions.",category:"partner",logo:`${t9}microsoft_azure.svg`,tags:["PII","Microsoft"],providerKey:"PresidioPII"},{id:"bedrock",name:"Bedrock Guardrail",description:"AWS Bedrock Guardrails for content filtering, topic avoidance, and sensitive information detection.",category:"partner",logo:`${t9}bedrock.svg`,tags:["AWS","Content Safety"],providerKey:"Bedrock"},{id:"lakera",name:"Lakera",description:"AI security platform protecting against prompt injections, data leakage, and harmful content.",category:"partner",logo:`${t9}lakeraai.jpeg`,tags:["Security","Prompt Injection"],providerKey:"Lakera"},{id:"openai_moderation",name:"OpenAI Moderation",description:"OpenAI's content moderation API for detecting harmful content across multiple categories.",category:"partner",logo:`${t9}openai_small.svg`,tags:["Content Moderation","OpenAI"]},{id:"google_model_armor",name:"Google Cloud Model Armor",description:"Google Cloud's model protection service for safe and responsible AI deployments.",category:"partner",logo:`${t9}google.svg`,tags:["Google Cloud","Safety"]},{id:"guardrails_ai",name:"Guardrails AI",description:"Open-source framework for adding structural, type, and quality guarantees to LLM outputs.",category:"partner",logo:`${t9}guardrails_ai.jpeg`,tags:["Open Source","Validation"]},{id:"zscaler",name:"Zscaler AI Guard",description:"Enterprise AI security from Zscaler for monitoring and protecting AI/ML workloads.",category:"partner",logo:`${t9}zscaler.svg`,tags:["Enterprise","Security"]},{id:"panw",name:"PANW Prisma AIRS",description:"Palo Alto Networks Prisma AI Runtime Security for securing AI applications in production.",category:"partner",logo:`${t9}palo_alto_networks.jpeg`,tags:["Enterprise","Security"]},{id:"cisco_ai_defense",name:"Cisco AI Defense",description:"Cisco AI Defense Inspection API for runtime protection: prompt injection, PII/PCI/PHI, harassment, hate speech, profanity, violence, and code detection.",category:"partner",logo:`${t9}cisco.png`,tags:["Enterprise","Security","Prompt Injection","PII"],providerKey:"CiscoAiDefense"},{id:"noma",name:"Noma Security",description:"AI security platform for detecting and preventing AI-specific threats and vulnerabilities.",category:"partner",logo:`${t9}noma_security.png`,tags:["Security","Threat Detection"]},{id:"aporia",name:"Aporia AI",description:"Real-time AI guardrails for hallucination detection, topic control, and policy enforcement.",category:"partner",logo:`${t9}aporia.png`,tags:["Hallucination","Policy"]},{id:"aim",name:"AIM Guardrail",description:"AIM Security guardrails for comprehensive AI threat detection and mitigation.",category:"partner",logo:`${t9}aim_security.jpeg`,tags:["Security","Threat Detection"]},{id:"cato_networks",name:"Cato Networks Guardrail",description:"Cato Networks guardrails for comprehensive AI threat detection and mitigation.",category:"partner",logo:`${t9}cato_networks.svg`,tags:["Security","Threat Detection"]},{id:"prompt_security",name:"Prompt Security",description:"Protect against prompt injection attacks, data leakage, and other LLM security threats.",category:"partner",logo:`${t9}prompt_security.png`,tags:["Prompt Injection","Security"]},{id:"lasso",name:"Lasso Guardrail",description:"Content moderation and safety guardrails for responsible AI deployments.",category:"partner",logo:`${t9}lasso.png`,tags:["Content Moderation"]},{id:"pangea",name:"Pangea Guardrail",description:"Pangea's AI guardrails for secure, compliant, and trustworthy AI applications.",category:"partner",logo:`${t9}pangea.png`,tags:["Compliance","Security"]},{id:"enkryptai",name:"EnkryptAI",description:"AI security and governance platform for enterprise AI safety and compliance.",category:"partner",logo:`${t9}enkrypt_ai.avif`,tags:["Enterprise","Governance"]},{id:"javelin",name:"Javelin Guardrails",description:"AI gateway with built-in guardrails for secure and compliant AI operations.",category:"partner",logo:`${t9}javelin.png`,tags:["Gateway","Security"]},{id:"pillar",name:"Pillar Guardrail",description:"AI safety platform for monitoring, testing, and securing AI systems.",category:"partner",logo:`${t9}pillar.jpeg`,tags:["Monitoring","Safety"]},{id:"akto",name:"Akto Guardrail",description:"AI security platform from Akto.io with automatic monitoring and guardrails for AI/ML applications.",category:"partner",logo:`${t9}akto.svg`,tags:["Security","Safety","Monitoring"]},{id:"promptguard",name:"PromptGuard",description:"AI security gateway with prompt injection detection, PII redaction, topic filtering, entity blocklists, and hallucination detection. Self-hostable with drop-in proxy integration.",category:"partner",logo:`${t9}promptguard.svg`,tags:["Security","Prompt Injection","PII"],providerKey:"Promptguard",eval:{f1:94.9,precision:100,recall:90.4,testCases:5384,latency:"~150ms"}},{id:"xecguard",name:"XecGuard",description:"CyCraft XecGuard AI security gateway. Multi-policy scanning (prompt injection, harmful content, PII, system-prompt enforcement) plus RAG context grounding.",category:"partner",logo:`${t9}xecguard.svg`,tags:["Security","Policy","Grounding","RAG"],providerKey:"Xecguard"},{id:"repelloai",name:"RepelloAI Argus",description:"RepelloAI Argus scans prompts and responses against policies configured per asset in the Repello dashboard.",category:"partner",logo:`${t9}repelloai.png`,tags:["Security","Policy","Prompt Injection"],providerKey:"Repelloai"}];var at=e.i(826910);let aa=({src:e,name:t})=>{let[a,i]=(0,r.useState)(!1);return a||!e?(0,l.jsx)("div",{style:{width:28,height:28,borderRadius:6,backgroundColor:"#e5e7eb",display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,fontWeight:600,color:"#6b7280",flexShrink:0},children:t?.charAt(0)||"?"}):(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(e),alt:"",style:{width:28,height:28,borderRadius:6,objectFit:"contain",flexShrink:0},onError:()=>i(!0)})},al=({card:e,onClick:t})=>{let[a,i]=(0,r.useState)(!1);return(0,l.jsxs)("div",{onClick:t,onMouseEnter:()=>i(!0),onMouseLeave:()=>i(!1),style:{borderRadius:12,border:a?"1px solid #93c5fd":"1px solid #e5e7eb",backgroundColor:"#ffffff",padding:"20px 20px 16px 20px",cursor:"pointer",transition:"border-color 0.15s, box-shadow 0.15s",display:"flex",flexDirection:"column",minHeight:170,boxShadow:a?"0 1px 6px rgba(59,130,246,0.08)":"none"},children:[(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:10},children:[(0,l.jsx)(aa,{src:e.logo,name:e.name}),(0,l.jsx)("span",{style:{fontSize:14,fontWeight:600,color:"#111827",lineHeight:1.3},children:e.name})]}),(0,l.jsx)("p",{className:"line-clamp-3",style:{fontSize:12,color:"#6b7280",lineHeight:1.6,margin:0,flex:1},children:e.description}),e.eval&&(0,l.jsxs)("div",{style:{marginTop:10,display:"flex",alignItems:"center",gap:4},children:[(0,l.jsx)(at.CheckCircleFilled,{style:{color:"#16a34a",fontSize:12}}),(0,l.jsxs)("span",{style:{fontSize:11,color:"#16a34a",fontWeight:500},children:["F1: ",e.eval.f1,"% · ",e.eval.testCases," test cases"]})]})]})};var ar=e.i(447566);let ai={cf_denied_financial:{provider:"LitellmContentFilter",categoryName:"denied_financial_advice",guardrailNameSuggestion:"Denied Financial Advice",mode:"pre_call",defaultOn:!1},cf_denied_legal:{provider:"LitellmContentFilter",categoryName:"denied_legal_advice",guardrailNameSuggestion:"Denied Legal Advice",mode:"pre_call",defaultOn:!1},cf_denied_medical:{provider:"LitellmContentFilter",categoryName:"denied_medical_advice",guardrailNameSuggestion:"Denied Medical Advice",mode:"pre_call",defaultOn:!1},cf_denied_insults:{provider:"LitellmContentFilter",categoryName:"denied_insults",guardrailNameSuggestion:"Insults & Personal Attacks",mode:"pre_call",defaultOn:!1},cf_harmful_violence:{provider:"LitellmContentFilter",categoryName:"harmful_violence",guardrailNameSuggestion:"Harmful Violence",mode:"pre_call",defaultOn:!1},cf_harmful_self_harm:{provider:"LitellmContentFilter",categoryName:"harmful_self_harm",guardrailNameSuggestion:"Harmful Self-Harm",mode:"pre_call",defaultOn:!1},cf_harmful_child_safety:{provider:"LitellmContentFilter",categoryName:"harmful_child_safety",guardrailNameSuggestion:"Harmful Child Safety",mode:"pre_call",defaultOn:!1},cf_harmful_illegal_weapons:{provider:"LitellmContentFilter",categoryName:"harmful_illegal_weapons",guardrailNameSuggestion:"Harmful Illegal Weapons",mode:"pre_call",defaultOn:!1},cf_bias_gender:{provider:"LitellmContentFilter",categoryName:"bias_gender",guardrailNameSuggestion:"Bias: Gender",mode:"pre_call",defaultOn:!1},cf_bias_racial:{provider:"LitellmContentFilter",categoryName:"bias_racial",guardrailNameSuggestion:"Bias: Racial",mode:"pre_call",defaultOn:!1},cf_bias_religious:{provider:"LitellmContentFilter",categoryName:"bias_religious",guardrailNameSuggestion:"Bias: Religious",mode:"pre_call",defaultOn:!1},cf_bias_sexual_orientation:{provider:"LitellmContentFilter",categoryName:"bias_sexual_orientation",guardrailNameSuggestion:"Bias: Sexual Orientation",mode:"pre_call",defaultOn:!1},cf_prompt_injection_jailbreak:{provider:"LitellmContentFilter",categoryName:"prompt_injection_jailbreak",guardrailNameSuggestion:"Prompt Injection: Jailbreak",mode:"pre_call",defaultOn:!1},cf_prompt_injection_data_exfil:{provider:"LitellmContentFilter",categoryName:"prompt_injection_data_exfiltration",guardrailNameSuggestion:"Prompt Injection: Data Exfiltration",mode:"pre_call",defaultOn:!1},cf_prompt_injection_sql:{provider:"LitellmContentFilter",categoryName:"prompt_injection_sql",guardrailNameSuggestion:"Prompt Injection: SQL",mode:"pre_call",defaultOn:!1},cf_prompt_injection_malicious_code:{provider:"LitellmContentFilter",categoryName:"prompt_injection_malicious_code",guardrailNameSuggestion:"Prompt Injection: Malicious Code",mode:"pre_call",defaultOn:!1},cf_prompt_injection_system_prompt:{provider:"LitellmContentFilter",categoryName:"prompt_injection_system_prompt",guardrailNameSuggestion:"Prompt Injection: System Prompt",mode:"pre_call",defaultOn:!1},cf_toxic_abuse:{provider:"LitellmContentFilter",categoryName:"harm_toxic_abuse",guardrailNameSuggestion:"Toxic & Abusive Language",mode:"pre_call",defaultOn:!1},cf_patterns:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Pattern Matching",mode:"pre_call",defaultOn:!1},cf_keywords:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Keyword Blocking",mode:"pre_call",defaultOn:!1},block_code_execution:{provider:"BlockCodeExecution",guardrailNameSuggestion:"Block Code Execution",mode:"pre_call",defaultOn:!1},cf_competitor_intent:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Competitor Name Blocking",mode:"pre_call",defaultOn:!1},presidio:{provider:"PresidioPII",guardrailNameSuggestion:"Presidio PII",mode:"pre_call",defaultOn:!1},bedrock:{provider:"Bedrock",guardrailNameSuggestion:"Bedrock Guardrail",mode:"pre_call",defaultOn:!1},lakera:{provider:"Lakera",guardrailNameSuggestion:"Lakera",mode:"pre_call",defaultOn:!1},openai_moderation:{provider:"OpenaiModeration",guardrailNameSuggestion:"OpenAI Moderation",mode:"pre_call",defaultOn:!1},google_model_armor:{provider:"ModelArmor",guardrailNameSuggestion:"Google Cloud Model Armor",mode:"pre_call",defaultOn:!1},guardrails_ai:{provider:"GuardrailsAi",guardrailNameSuggestion:"Guardrails AI",mode:"pre_call",defaultOn:!1},zscaler:{provider:"ZscalerAiGuard",guardrailNameSuggestion:"Zscaler AI Guard",mode:"pre_call",defaultOn:!1},panw:{provider:"PanwPrismaAirs",guardrailNameSuggestion:"PANW Prisma AIRS",mode:"pre_call",defaultOn:!1},cisco_ai_defense:{provider:"CiscoAiDefense",guardrailNameSuggestion:"Cisco AI Defense",mode:"pre_call",defaultOn:!1},noma:{provider:"Noma",guardrailNameSuggestion:"Noma Security",mode:"pre_call",defaultOn:!1},aporia:{provider:"AporiaAi",guardrailNameSuggestion:"Aporia AI",mode:"pre_call",defaultOn:!1},aim:{provider:"Aim",guardrailNameSuggestion:"AIM Guardrail",mode:"pre_call",defaultOn:!1},cato_networks:{provider:"Cato Networks",guardrailNameSuggestion:"Cato Networks Guardrail",mode:"pre_call",defaultOn:!1},prompt_security:{provider:"PromptSecurity",guardrailNameSuggestion:"Prompt Security",mode:"pre_call",defaultOn:!1},lasso:{provider:"Lasso",guardrailNameSuggestion:"Lasso Guardrail",mode:"pre_call",defaultOn:!1},pangea:{provider:"Pangea",guardrailNameSuggestion:"Pangea Guardrail",mode:"pre_call",defaultOn:!1},enkryptai:{provider:"Enkryptai",guardrailNameSuggestion:"EnkryptAI",mode:"pre_call",defaultOn:!1},javelin:{provider:"Javelin",guardrailNameSuggestion:"Javelin Guardrails",mode:"pre_call",defaultOn:!1},pillar:{provider:"Pillar",guardrailNameSuggestion:"Pillar Guardrail",mode:"pre_call",defaultOn:!1},akto:{provider:"Akto",guardrailNameSuggestion:"Akto Guardrail",mode:"pre_call",defaultOn:!1},promptguard:{provider:"Promptguard",guardrailNameSuggestion:"PromptGuard",mode:"pre_call",defaultOn:!1},xecguard:{provider:"Xecguard",guardrailNameSuggestion:"XecGuard",mode:"pre_call",defaultOn:!1},repelloai:{provider:"Repelloai",guardrailNameSuggestion:"RepelloAI Argus",mode:"pre_call",defaultOn:!1}},as=({card:e,onBack:t,accessToken:a,onGuardrailCreated:s})=>{let[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("overview"),m=[{property:"Provider",value:"litellm"===e.category?"LiteLLM Content Filter":"Partner Guardrail"},...e.subcategory?[{property:"Subcategory",value:e.subcategory}]:[],..."litellm"===e.category?[{property:"Cost",value:"$0 / request"}]:[],..."litellm"===e.category?[{property:"External Dependencies",value:"None"}]:[],..."litellm"===e.category?[{property:"Latency",value:e.eval?.latency||"<1ms"}]:[]],u=e.eval?[{metric:"Precision",value:`${e.eval.precision}%`},{metric:"Recall",value:`${e.eval.recall}%`},{metric:"F1 Score",value:`${e.eval.f1}%`},{metric:"Test Cases",value:String(e.eval.testCases)},{metric:"False Positives",value:"0"},{metric:"False Negatives",value:"0"},{metric:"Latency (p50)",value:e.eval.latency}]:[],p=[{key:"overview",label:"Overview"},...e.eval?[{key:"eval",label:"Eval Results"}]:[]];return(0,l.jsxs)("div",{style:{maxWidth:960,margin:"0 auto"},children:[(0,l.jsxs)("div",{onClick:t,style:{display:"inline-flex",alignItems:"center",gap:6,color:"#5f6368",cursor:"pointer",fontSize:14,marginBottom:24},children:[(0,l.jsx)(ar.ArrowLeftOutlined,{style:{fontSize:11}}),(0,l.jsx)("span",{children:e.name})]}),(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16,marginBottom:8},children:[(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(e.logo),alt:"",style:{width:40,height:40,borderRadius:8,objectFit:"contain"},onError:e=>{e.target.style.display="none"}}),(0,l.jsx)("h1",{style:{fontSize:28,fontWeight:400,color:"#202124",margin:0,lineHeight:1.2},children:e.name})]}),(0,l.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 20px 0",lineHeight:1.6},children:e.description}),(0,l.jsx)("div",{style:{display:"flex",gap:10,marginBottom:32},children:(0,l.jsx)(i.Button,{onClick:()=>o(!0),style:{borderRadius:20,padding:"4px 20px",height:36,borderColor:"#dadce0",color:"#1a73e8",fontWeight:500,fontSize:14},children:"Create Guardrail"})}),(0,l.jsx)("div",{style:{borderBottom:"1px solid #dadce0",marginBottom:28},children:(0,l.jsx)("div",{style:{display:"flex",gap:0},children:p.map(e=>(0,l.jsx)("div",{onClick:()=>c(e.key),style:{padding:"12px 20px",fontSize:14,color:d===e.key?"#1a73e8":"#5f6368",borderBottom:d===e.key?"3px solid #1a73e8":"3px solid transparent",cursor:"pointer",fontWeight:d===e.key?500:400,marginBottom:-1},children:e.label},e.key))})}),"overview"===d&&(0,l.jsxs)("div",{style:{display:"flex",gap:64},children:[(0,l.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 12px 0"},children:"Overview"}),(0,l.jsx)("p",{style:{fontSize:14,color:"#3c4043",lineHeight:1.7,margin:"0 0 32px 0"},children:e.description}),(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 4px 0"},children:"Guardrail Details"}),(0,l.jsx)("p",{style:{fontSize:13,color:"#5f6368",margin:"0 0 16px 0"},children:"Details are as follows"}),(0,l.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:14},children:[(0,l.jsx)("thead",{children:(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #dadce0"},children:[(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500,width:200},children:"Property"}),(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500},children:e.name})]})}),(0,l.jsx)("tbody",{children:m.map((e,t)=>(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,l.jsx)("td",{style:{padding:"12px 0",color:"#3c4043"},children:e.property}),(0,l.jsx)("td",{style:{padding:"12px 0",color:"#202124"},children:e.value})]},t))})]})]}),(0,l.jsxs)("div",{style:{width:240,flexShrink:0},children:[(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Guardrail ID"}),(0,l.jsxs)("div",{style:{fontSize:13,color:"#202124",wordBreak:"break-all"},children:["litellm/",e.id]})]}),(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Type"}),(0,l.jsx)("div",{style:{fontSize:13,color:"#202124"},children:"litellm"===e.category?"Content Filter":"Partner"})]}),e.tags.length>0&&(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:8},children:"Tags"}),(0,l.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:6},children:e.tags.map(e=>(0,l.jsx)("span",{style:{fontSize:12,padding:"4px 12px",borderRadius:16,border:"1px solid #dadce0",color:"#3c4043",backgroundColor:"#fff"},children:e},e))})]})]})]}),"eval"===d&&(0,l.jsxs)("div",{children:[(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 16px 0"},children:"Eval Results"}),(0,l.jsxs)("table",{style:{width:"100%",maxWidth:560,borderCollapse:"collapse",fontSize:14},children:[(0,l.jsx)("thead",{children:(0,l.jsxs)("tr",{style:{backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 16px",color:"#5f6368",fontWeight:500},children:"Metric"}),(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 16px",color:"#5f6368",fontWeight:500},children:"Value"})]})}),(0,l.jsx)("tbody",{children:u.map((e,t)=>(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,l.jsx)("td",{style:{padding:"12px 16px",color:"#3c4043"},children:e.metric}),(0,l.jsx)("td",{style:{padding:"12px 16px",color:"#202124",fontWeight:500},children:e.value})]},t))})]})]}),(0,l.jsx)(e1,{visible:n,onClose:()=>o(!1),accessToken:a,onSuccess:()=>{o(!1),s()},preset:ai[e.id]})]})},an=({accessToken:e,onGuardrailCreated:t})=>{let[a,i]=(0,r.useState)(""),[s,n]=(0,r.useState)(null),[o,d]=(0,r.useState)(!1),c=ae.filter(e=>{if(!a)return!0;let t=a.toLowerCase();return e.name.toLowerCase().includes(t)||e.description.toLowerCase().includes(t)||e.tags.some(e=>e.toLowerCase().includes(t))}),m=c.filter(e=>"litellm"===e.category),u=c.filter(e=>"partner"===e.category);return s?(0,l.jsx)(as,{card:s,onBack:()=>n(null),accessToken:e,onGuardrailCreated:t}):(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{marginBottom:24},children:(0,l.jsx)(p.Input,{size:"large",placeholder:"Search guardrails",prefix:(0,l.jsx)(tQ.SearchOutlined,{style:{color:"#9ca3af"}}),value:a,onChange:e=>i(e.target.value),style:{borderRadius:8}})}),(0,l.jsxs)("div",{style:{marginBottom:40},children:[(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:4},children:[(0,l.jsx)("h2",{style:{fontSize:20,fontWeight:600,color:"#111827",margin:0},children:"LiteLLM Content Filter"}),(0,l.jsx)("span",{style:{display:"inline-flex",alignItems:"center",gap:6,fontSize:14,color:"#1a73e8",cursor:"pointer"},onClick:()=>d(!o),children:o?(0,l.jsx)(l.Fragment,{children:"Show less"}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(t7.ArrowRightOutlined,{style:{fontSize:12}}),`Show all (${m.length})`]})})]}),(0,l.jsx)("p",{style:{fontSize:13,color:"#6b7280",margin:"4px 0 20px 0"},children:"Built-in guardrails powered by LiteLLM. Zero latency, no external dependencies, no additional cost."}),(0,l.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:16},children:(o?m:m.slice(0,10)).map(e=>(0,l.jsx)(al,{card:e,onClick:()=>n(e)},e.id))})]}),(0,l.jsxs)("div",{style:{marginBottom:40},children:[(0,l.jsx)("h2",{style:{fontSize:20,fontWeight:600,color:"#111827",margin:"0 0 4px 0"},children:"Partner Guardrails"}),(0,l.jsx)("p",{style:{fontSize:13,color:"#6b7280",margin:"4px 0 20px 0"},children:"Third-party guardrail integrations from leading AI security providers."}),(0,l.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:16},children:u.map(e=>(0,l.jsx)(al,{card:e,onClick:()=>n(e)},e.id))})]})]})};var ao=e.i(988846),ad=e.i(837007),ac=e.i(409797),am=e.i(54131),au=e.i(995926),ap=e.i(634831),ag=e.i(438100),ax=e.i(302202),ah=e.i(328196),af=e.i(168118),ay=e.i(663435),aj=e.i(954616),a_=e.i(912598),ab=e.i(431703),av=e.i(135214),aw=e.i(243652);let aN=async(e,t)=>{let a=(0,m.getProxyBaseUrl)(),l=`${a}/guardrails/register`,r=await fetch(l,{method:"POST",headers:{[(0,m.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){let e=await r.json().catch(()=>({})),t=(0,ab.deriveErrorMessage)(e);throw(0,m.handleError)(t),Error(t)}return r.json()},aC=(0,aw.createQueryKeys)("guardrails");function aS(e){var t;let a=e.litellm_params??{},l=e.guardrail_info??{},r=a.headers,i=Array.isArray(r)?r.map(e=>({key:(e.key??e.name??"").toString(),value:String(e.value??"")})):"object"==typeof r&&null!==r?Object.entries(r).map(([e,t])=>({key:e,value:String(t??"")})):[],s=a.api_base??a.url??"",n=l.model??a.model??"—",o=a.forward_api_key??!0,d=Array.isArray(a.extra_headers)?a.extra_headers.filter(e=>"string"==typeof e):[];return{id:e.guardrail_id,team:e.team_id??"—",name:e.guardrail_name,endpoint:s,status:"pending_review"===(t=e.status)?"pending":"active"===t||"rejected"===t?t:"active",model:n,forwardKey:o,description:l.description??"",method:a.method??"POST",customHeaders:i,extraHeaders:d,submittedAt:function(e){if(!e)return"—";try{let t=new Date(e);return isNaN(t.getTime())?e:t.toISOString().slice(0,10)}catch{return e}}(e.submitted_at),submittedBy:e.submitted_by_email??e.submitted_by_user_id??"—",mode:a.mode,unreachable_fallback:a.unreachable_fallback,additionalProviderParams:a.additional_provider_specific_params,guardrailType:a.guardrail}}let ak={active:{label:"Active",bg:"bg-green-50",text:"text-green-700",dot:"bg-green-500"},pending:{label:"Pending Review",bg:"bg-yellow-50",text:"text-yellow-700",dot:"bg-yellow-500"},rejected:{label:"Rejected",bg:"bg-red-50",text:"text-red-700",dot:"bg-red-500"}},aI={"ML Platform":"bg-purple-100 text-purple-700","Data Science":"bg-blue-100 text-blue-700",Security:"bg-red-100 text-red-700","Customer Success":"bg-orange-100 text-orange-700",Legal:"bg-gray-100 text-gray-700",Finance:"bg-green-100 text-green-700"};function aA({label:e,value:t,color:a}){return(0,l.jsxs)("div",{className:"bg-white border border-gray-200 rounded-lg px-4 py-3",children:[(0,l.jsx)("div",{className:`text-2xl font-bold ${a}`,children:t}),(0,l.jsx)("div",{className:"text-xs text-gray-500 mt-0.5",children:e})]})}function aO({enabled:e,onToggle:t}){return(0,l.jsx)("button",{type:"button",onClick:t,role:"switch","aria-checked":e,className:`relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 ${e?"bg-blue-500":"bg-gray-200"}`,children:(0,l.jsx)("span",{className:`inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow transition-transform ${e?"translate-x-4":"translate-x-0.5"}`})})}function aT({guardrail:e,isSelected:t,isHeadersExpanded:a,onSelect:r,onToggleForwardKey:i,onToggleHeaders:s,onApprove:n,onReject:o}){let d=ak[e.status],c=aI[e.team]??"bg-gray-100 text-gray-700";return(0,l.jsxs)("div",{className:`bg-white border rounded-lg p-4 transition-all ${t?"border-blue-400 ring-1 ring-blue-200":"border-gray-200"}`,children:[(0,l.jsxs)("div",{className:"flex items-start justify-between gap-4",children:[(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-1.5 flex-wrap",children:[(0,l.jsxs)("span",{className:`text-xs font-medium px-2 py-0.5 rounded-full ${c}`,children:["Team: ",e.team]}),(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded-full ${d.bg} ${d.text}`,children:[(0,l.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${d.dot}`}),d.label]})]}),(0,l.jsx)("h3",{className:"text-sm font-semibold text-gray-900 mb-1",children:e.name}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mb-2 line-clamp-1",children:e.description}),(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)(ax.ServerIcon,{className:"h-3.5 w-3.5 text-gray-400 shrink-0"}),(0,l.jsx)("code",{className:"text-xs text-gray-500 font-mono truncate",children:e.endpoint})]}),(0,l.jsxs)("div",{className:"flex items-center gap-4 text-xs text-gray-500",children:[(0,l.jsxs)("span",{children:["Model: ",(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e.model})]}),(0,l.jsxs)("span",{children:["Submitted: ",(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e.submittedAt})]})]})]}),(0,l.jsxs)("div",{className:"flex flex-col items-end gap-2 shrink-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"text-xs text-gray-500 whitespace-nowrap",children:"Forward API Key"}),(0,l.jsx)(aO,{enabled:e.forwardKey,onToggle:i})]}),(0,l.jsxs)("div",{className:"flex items-center gap-2 mt-1",children:[(0,l.jsx)("button",{type:"button",onClick:r,className:"text-xs border border-gray-300 text-gray-600 hover:bg-gray-50 px-3 py-1.5 rounded-md transition-colors font-medium",children:t?"Close":"Review"}),"pending"===e.status&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("button",{type:"button",onClick:n,className:"text-xs bg-green-500 hover:bg-green-600 text-white px-3 py-1.5 rounded-md transition-colors font-medium",children:"Approve"}),(0,l.jsx)("button",{type:"button",onClick:o,className:"text-xs border border-red-300 text-red-600 hover:bg-red-50 px-3 py-1.5 rounded-md transition-colors font-medium",children:"Reject"})]})]})]})]}),(0,l.jsxs)("div",{className:"mt-3 pt-3 border-t border-gray-100",children:[(0,l.jsxs)("button",{type:"button",onClick:s,className:"flex items-center gap-1.5 text-xs text-gray-500 hover:text-gray-700 transition-colors",children:[a?(0,l.jsx)(am.ChevronUpIcon,{className:"h-3.5 w-3.5"}):(0,l.jsx)(ac.ChevronDownIcon,{className:"h-3.5 w-3.5"}),"Static headers",e.customHeaders.length>0&&(0,l.jsx)("span",{className:"ml-1 bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.customHeaders.length})]}),a&&(0,l.jsx)("div",{className:"mt-2",children:0===e.customHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic",children:"No static headers configured."}):(0,l.jsx)("div",{className:"space-y-1",children:e.customHeaders.map((e,t)=>(0,l.jsxs)("div",{className:"flex items-center gap-2 text-xs font-mono",children:[(0,l.jsx)("span",{className:"text-gray-500 bg-gray-50 border border-gray-200 rounded-sm px-2 py-0.5",children:e.key}),(0,l.jsx)("span",{className:"text-gray-400",children:":"}),(0,l.jsx)("span",{className:"text-gray-700 bg-gray-50 border border-gray-200 rounded-sm px-2 py-0.5",children:e.value})]},`${e.key}-${t}`))})})]})]})}function aP({label:e,children:t}){return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"text-xs font-semibold text-gray-500 mb-1",children:e}),(0,l.jsx)("div",{children:t})]})}function aL({guardrail:e,onClose:t,onApprove:a,onReject:i,onToggleForwardKey:s,onUpdateCustomHeaders:n,onUpdateExtraHeaders:o}){let[d,c]=(0,r.useState)(!1),[m,u]=(0,r.useState)(""),[p,g]=(0,r.useState)(""),[x,h]=(0,r.useState)(""),f=ak[e.status],y=aI[e.team]??"bg-gray-100 text-gray-700";return(0,l.jsx)("div",{className:"w-96 shrink-0 bg-white overflow-auto",children:(0,l.jsxs)("div",{className:"p-5",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between mb-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-1",children:[(0,l.jsxs)("span",{className:`text-xs font-medium px-2 py-0.5 rounded-full ${y}`,children:["Team: ",e.team]}),(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded-full ${f.bg} ${f.text}`,children:[(0,l.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${f.dot}`}),f.label]})]}),(0,l.jsx)("h2",{className:"text-base font-semibold text-gray-900",children:e.name}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:["Submitted by ",e.submittedBy," on ",e.submittedAt]})]}),(0,l.jsx)("button",{type:"button",onClick:t,className:"text-gray-400 hover:text-gray-600 transition-colors","aria-label":"Close detail panel",children:(0,l.jsx)(au.XIcon,{className:"h-4 w-4"})})]}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mb-5",children:e.description}),(0,l.jsxs)("div",{className:"space-y-4",children:[(0,l.jsx)(aP,{label:"Endpoint",children:(0,l.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,l.jsx)("code",{className:"text-xs font-mono text-gray-700 break-all",children:e.endpoint}),(0,l.jsx)("a",{href:e.endpoint,target:"_blank",rel:"noopener noreferrer",className:"text-gray-400 hover:text-blue-500 shrink-0",children:(0,l.jsx)(ap.ExternalLinkIcon,{className:"h-3.5 w-3.5"})})]})}),(0,l.jsx)(aP,{label:"Method",children:(0,l.jsx)("span",{className:"text-xs font-mono font-medium text-gray-700 bg-gray-100 px-2 py-0.5 rounded-sm",children:e.method})}),(0,l.jsxs)("div",{className:"border border-blue-100 bg-blue-50 rounded-lg p-3",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,l.jsx)(ag.KeyIcon,{className:"h-3.5 w-3.5 text-blue-500"}),(0,l.jsx)("span",{className:"text-xs font-semibold text-blue-800",children:"Forward LiteLLM API Key"})]}),(0,l.jsx)(aO,{enabled:e.forwardKey,onToggle:s})]}),(0,l.jsxs)("p",{className:"text-xs text-blue-700 leading-relaxed",children:["When enabled, the caller's LiteLLM API key is forwarded as an"," ",(0,l.jsx)("code",{className:"font-mono bg-blue-100 px-1 rounded-sm",children:"Authorization"}),"header to your guardrail endpoint. This allows your guardrail to authenticate model calls using the original caller's credentials."]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-700",children:"Static headers"}),e.customHeaders.length>0&&(0,l.jsx)("span",{className:"bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.customHeaders.length})]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Sent with every request to the guardrail."}),0===e.customHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic mb-2",children:"No static headers configured."}):(0,l.jsx)("ul",{className:"list-none space-y-1 mb-2",children:e.customHeaders.map((t,a)=>(0,l.jsxs)("li",{className:"flex items-center justify-between gap-2 text-xs font-mono bg-gray-50 border border-gray-200 rounded-sm px-2 py-1.5",children:[(0,l.jsxs)("span",{className:"text-gray-700 truncate",children:[t.key,": ",t.value]}),(0,l.jsx)("button",{type:"button",onClick:()=>n(e.customHeaders.filter((e,t)=>t!==a)),className:"text-gray-400 hover:text-red-600 shrink-0","aria-label":`Remove ${t.key}`,children:(0,l.jsx)(au.XIcon,{className:"h-3.5 w-3.5"})})]},`${t.key}-${a}`))}),(0,l.jsxs)("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-end",children:[(0,l.jsx)("input",{type:"text",value:p,onChange:e=>g(e.target.value),placeholder:"Header name (e.g. X-API-Key)",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=p.trim(),l=x.trim();a&&!e.customHeaders.some(e=>e.key.toLowerCase()===a.toLowerCase())&&(n([...e.customHeaders,{key:a,value:l}]),g(""),h(""))}}}),(0,l.jsx)("input",{type:"text",value:x,onChange:e=>h(e.target.value),placeholder:"Value",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=p.trim(),l=x.trim();a&&!e.customHeaders.some(e=>e.key.toLowerCase()===a.toLowerCase())&&(n([...e.customHeaders,{key:a,value:l}]),g(""),h(""))}}}),(0,l.jsx)("button",{type:"button",onClick:()=>{let t=p.trim(),a=x.trim();t&&!e.customHeaders.some(e=>e.key.toLowerCase()===t.toLowerCase())&&(n([...e.customHeaders,{key:t,value:a}]),g(""),h(""))},className:"text-xs font-medium text-blue-600 hover:text-blue-700 border border-blue-200 bg-blue-50 hover:bg-blue-100 px-2 py-1.5 rounded-sm transition-colors shrink-0",children:"Add"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-700",children:"Forward client headers"}),e.extraHeaders.length>0&&(0,l.jsx)("span",{className:"bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.extraHeaders.length})]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Allowed header names to forward from the client request to the guardrail (e.g. x-request-id)."}),0===e.extraHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic mb-2",children:"No forward client headers configured."}):(0,l.jsx)("ul",{className:"list-none space-y-1 mb-2",children:e.extraHeaders.map((t,a)=>(0,l.jsxs)("li",{className:"flex items-center justify-between gap-2 text-xs font-mono bg-gray-50 border border-gray-200 rounded-sm px-2 py-1.5",children:[(0,l.jsx)("span",{className:"text-gray-700 truncate",children:t}),(0,l.jsx)("button",{type:"button",onClick:()=>o(e.extraHeaders.filter((e,t)=>t!==a)),className:"text-gray-400 hover:text-red-600 shrink-0","aria-label":`Remove ${t}`,children:(0,l.jsx)(au.XIcon,{className:"h-3.5 w-3.5"})})]},`${t}-${a}`))}),(0,l.jsxs)("div",{className:"flex gap-2",children:[(0,l.jsx)("input",{type:"text",value:m,onChange:e=>u(e.target.value),placeholder:"e.g. x-request-id",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=m.trim().toLowerCase();a&&!e.extraHeaders.map(e=>e.toLowerCase()).includes(a)&&(o([...e.extraHeaders,a]),u(""))}}}),(0,l.jsx)("button",{type:"button",onClick:()=>{let t=m.trim().toLowerCase();t&&!e.extraHeaders.map(e=>e.toLowerCase()).includes(t)&&(o([...e.extraHeaders,t]),u(""))},className:"text-xs font-medium text-blue-600 hover:text-blue-700 border border-blue-200 bg-blue-50 hover:bg-blue-100 px-2 py-1.5 rounded-sm transition-colors",children:"Add"})]})]}),(0,l.jsxs)("div",{className:"border border-gray-200 rounded-lg overflow-hidden",children:[(0,l.jsxs)("button",{type:"button",onClick:()=>c(!d),className:"w-full flex items-center justify-between px-3 py-2 text-left text-xs font-semibold text-gray-700 bg-gray-50 hover:bg-gray-100 transition-colors",children:[(0,l.jsx)("span",{children:"Equivalent config"}),d?(0,l.jsx)(am.ChevronUpIcon,{className:"h-3.5 w-3.5 text-gray-500"}):(0,l.jsx)(ac.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-500"})]}),d&&(0,l.jsx)("pre",{className:"p-3 text-xs font-mono text-gray-700 bg-white border-t border-gray-200 overflow-x-auto whitespace-pre-wrap break-all",children:function(e){let t=["litellm_settings:"," guardrails:",` - guardrail_name: "${e.name.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`," litellm_params:",` guardrail: ${e.guardrailType??"generic_guardrail_api"}`,` mode: ${e.mode??"pre_call"} # or post_call, during_call`,` api_base: ${e.endpoint||"https://your-guardrail-api.com"}`," api_key: os.environ/YOUR_GUARDRAIL_API_KEY # optional",` unreachable_fallback: ${e.unreachable_fallback??"fail_closed"} # default: fail_closed. Set to fail_open to proceed if the guardrail endpoint is unreachable.`,` forward_api_key: ${e.forwardKey}`];if(e.model&&"—"!==e.model&&t.push(` model: "${e.model}" # LLM model name sent to the guardrail for context`),e.customHeaders.length>0)for(let a of(t.push(" headers: # static headers (sent with every request)"),e.customHeaders))t.push(` ${a.key}: "${String(a.value).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`);if(e.extraHeaders.length>0)for(let a of(t.push(" extra_headers: # forward these client request headers to the guardrail"),e.extraHeaders))t.push(` - ${a}`);if(e.additionalProviderParams&&Object.keys(e.additionalProviderParams).length>0)for(let[a,l]of(t.push(" additional_provider_specific_params:"),Object.entries(e.additionalProviderParams))){let e="string"==typeof l?`"${l}"`:String(l);t.push(` ${a}: ${e}`)}return t.join("\n")}(e)})]}),(0,l.jsxs)("div",{className:"flex items-start gap-2 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,l.jsx)(af.InfoIcon,{className:"h-3.5 w-3.5 text-gray-400 shrink-0 mt-0.5"}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 leading-relaxed",children:["This guardrail runs on a separate instance. It receives the user request and forwards the result to the next step in the pipeline. See"," ",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/adding_provider/generic_guardrail_api",target:"_blank",rel:"noopener noreferrer",className:"text-blue-500 hover:underline",children:"LiteLLM Generic Guardrail API docs"})," ","for configuration details."]})]})]}),(0,l.jsxs)("div",{className:"mt-5 pt-4 border-t border-gray-100 space-y-2",children:[(0,l.jsxs)("button",{type:"button",className:"w-full flex items-center justify-center gap-2 border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(ap.ExternalLinkIcon,{className:"h-4 w-4"}),"Test Endpoint"]}),"pending"===e.status&&(0,l.jsxs)("div",{className:"flex gap-2",children:[(0,l.jsxs)("button",{type:"button",onClick:a,className:"flex-1 flex items-center justify-center gap-1.5 bg-green-500 hover:bg-green-600 text-white text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(tN.CheckIcon,{className:"h-4 w-4"}),"Approve"]}),(0,l.jsxs)("button",{type:"button",onClick:i,className:"flex-1 flex items-center justify-center gap-1.5 border border-red-300 text-red-600 hover:bg-red-50 text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(au.XIcon,{className:"h-4 w-4"}),"Reject"]})]})]})]})})}function aB({action:e,guardrailName:t,onConfirm:a,onCancel:r}){let i="approve"===e;return(0,l.jsx)("div",{className:"fixed inset-0 bg-black/30 flex items-center justify-center z-50",children:(0,l.jsxs)("div",{className:"bg-white rounded-xl shadow-xl p-6 max-w-sm w-full mx-4",children:[(0,l.jsx)("div",{className:`w-10 h-10 rounded-full flex items-center justify-center mb-4 ${i?"bg-green-100":"bg-red-100"}`,children:i?(0,l.jsx)(tN.CheckIcon,{className:"h-5 w-5 text-green-600"}):(0,l.jsx)(ah.AlertCircleIcon,{className:"h-5 w-5 text-red-600"})}),(0,l.jsx)("h3",{className:"text-base font-semibold text-gray-900 mb-1",children:i?"Approve Guardrail":"Reject Guardrail"}),(0,l.jsxs)("p",{className:"text-sm text-gray-500 mb-5",children:["Are you sure you want to ",e," ",(0,l.jsxs)("span",{className:"font-medium text-gray-700",children:['"',t,'"']}),"?"," ",i?"This will make it active and available for use.":"This will mark it as rejected and notify the team."]}),(0,l.jsxs)("div",{className:"flex gap-3",children:[(0,l.jsx)("button",{type:"button",onClick:r,className:"flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium py-2 rounded-md transition-colors",children:"Cancel"}),(0,l.jsx)("button",{type:"button",onClick:a,className:`flex-1 text-white text-sm font-medium py-2 rounded-md transition-colors ${i?"bg-green-500 hover:bg-green-600":"bg-red-500 hover:bg-red-600"}`,children:i?"Approve":"Reject"})]})]})})}function aF({accessToken:e}){let[t,a]=(0,r.useState)([]),[i,s]=(0,r.useState)({total:0,pending_review:0,active:0,rejected:0}),[n,o]=(0,r.useState)(""),[d,c]=(0,r.useState)("all"),[h,f]=(0,r.useState)(null),[j,_]=(0,r.useState)(new Set),[b,v]=(0,r.useState)(null),[w,N]=(0,r.useState)(!0),[C,S]=(0,r.useState)(null),[k,I]=(0,r.useState)(""),[A,O]=(0,r.useState)(!1),[T]=u.Form.useForm(),P=(()=>{let{accessToken:e}=(0,av.default)(),t=(0,a_.useQueryClient)();return(0,aj.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return aN(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:aC.all})}})})();(0,r.useEffect)(()=>{let e=setTimeout(()=>I(n),300);return()=>clearTimeout(e)},[n]);let L=(0,r.useCallback)(async()=>{if(!e)return void N(!1);N(!0),S(null);try{let t="all"===d?void 0:"pending"===d?"pending_review":d,l=await (0,m.listGuardrailSubmissions)(e,{status:t,search:k.trim()||void 0});a(l.submissions.map(aS)),s(l.summary)}catch(e){S(e instanceof Error?e.message:"Failed to load submissions"),a([])}finally{N(!1)}},[e,d,k]);(0,r.useEffect)(()=>{L()},[L]);let B=t.find(e=>e.id===h)??null,F=i.total,$=i.pending_review,E=i.active,M=i.rejected;async function R(l){if(!e)return;let r=t.find(e=>e.id===l);if(!r)return;let i=!r.forwardKey;try{await (0,m.updateGuardrailCall)(e,l,{litellm_params:{forward_api_key:i}}),a(e=>e.map(e=>e.id===l?{...e,forwardKey:i}:e)),y.default.success(i?"Forward API key enabled":"Forward API key disabled")}catch{y.default.fromBackend("Failed to update forward API key")}}async function G(t,l){if(!e)return;let r={};for(let{key:e,value:t}of l)e.trim()&&(r[e.trim()]=t);try{await (0,m.updateGuardrailCall)(e,t,{litellm_params:{headers:r}}),a(e=>e.map(e=>e.id===t?{...e,customHeaders:l.filter(e=>e.key.trim())}:e)),y.default.success("Static headers updated")}catch{y.default.fromBackend("Failed to update static headers")}}async function z(t,l){if(e)try{await (0,m.updateGuardrailCall)(e,t,{litellm_params:{extra_headers:l}}),a(e=>e.map(e=>e.id===t?{...e,extraHeaders:l}:e)),y.default.success("Forward client headers updated")}catch{y.default.fromBackend("Failed to update forward client headers")}}async function D(t){if(e)try{await (0,m.approveGuardrailSubmission)(e,t),v(null),h===t&&f(null),await L(),y.default.success("Guardrail approved")}catch{y.default.fromBackend("Failed to approve guardrail")}}async function K(t){if(e)try{await (0,m.rejectGuardrailSubmission)(e,t),v(null),h===t&&f(null),await L(),y.default.success("Guardrail rejected")}catch{y.default.fromBackend("Failed to reject guardrail")}}return(0,l.jsxs)("div",{className:"flex h-full",children:[(0,l.jsxs)("div",{className:`flex-1 min-w-0 p-6 overflow-auto ${B?"border-r border-gray-200":""}`,children:[(0,l.jsxs)("div",{className:"grid grid-cols-4 gap-4 mb-6",children:[(0,l.jsx)(aA,{label:"Total Submitted",value:F,color:"text-gray-900"}),(0,l.jsx)(aA,{label:"Pending Review",value:$,color:"text-yellow-600"}),(0,l.jsx)(aA,{label:"Active",value:E,color:"text-green-600"}),(0,l.jsx)(aA,{label:"Rejected",value:M,color:"text-red-600"})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3 mb-5",children:[(0,l.jsxs)("div",{className:"relative flex-1 max-w-xs",children:[(0,l.jsx)(ao.SearchIcon,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400"}),(0,l.jsx)("input",{type:"text",placeholder:"Search guardrails...",value:n,onChange:e=>o(e.target.value),className:"w-full pl-9 pr-4 py-2 border border-gray-200 rounded-md text-sm text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500"})]}),(0,l.jsxs)("select",{value:d,onChange:e=>c(e.target.value),className:"border border-gray-200 rounded-md px-3 py-2 text-sm text-gray-700 focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500 bg-white",children:[(0,l.jsx)("option",{value:"all",children:"All Status"}),(0,l.jsx)("option",{value:"pending",children:"Pending Review"}),(0,l.jsx)("option",{value:"active",children:"Active"}),(0,l.jsx)("option",{value:"rejected",children:"Rejected"})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>O(!0),className:"ml-auto flex items-center gap-2 bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium px-4 py-2 rounded-md transition-colors",children:[(0,l.jsx)(ad.PlusIcon,{className:"h-4 w-4"}),"Add Guardrail"]})]}),(0,l.jsxs)("div",{className:"space-y-3",children:[w&&(0,l.jsx)("div",{className:"text-center py-12 text-gray-500 text-sm",children:"Loading submissions…"}),C&&(0,l.jsx)("div",{className:"text-center py-12 text-red-600 text-sm",children:C}),!w&&!C&&0===t.length&&(0,l.jsx)("div",{className:"text-center py-12 text-gray-400 text-sm",children:"No guardrails match your filters."}),!w&&!C&&t.map(e=>(0,l.jsx)(aT,{guardrail:e,isSelected:h===e.id,isHeadersExpanded:j.has(e.id),onSelect:()=>f(h===e.id?null:e.id),onToggleForwardKey:()=>R(e.id),onToggleHeaders:()=>{var t;return t=e.id,void _(e=>{let a=new Set(e);return a.has(t)?a.delete(t):a.add(t),a})},onApprove:()=>v({id:e.id,action:"approve"}),onReject:()=>v({id:e.id,action:"reject"})},e.id))]})]}),B&&(0,l.jsx)(aL,{guardrail:B,onClose:()=>f(null),onApprove:()=>v({id:B.id,action:"approve"}),onReject:()=>v({id:B.id,action:"reject"}),onToggleForwardKey:()=>R(B.id),onUpdateCustomHeaders:e=>G(B.id,e),onUpdateExtraHeaders:e=>z(B.id,e)}),b&&(0,l.jsx)(aB,{action:b.action,guardrailName:t.find(e=>e.id===b.id)?.name??"",onConfirm:()=>"approve"===b.action?D(b.id):K(b.id),onCancel:()=>v(null)}),(0,l.jsxs)(g.Modal,{title:"Submit Guardrail for Review",open:A,onCancel:()=>{O(!1),T.resetFields()},onOk:()=>T.submit(),okText:"Submit for Review",children:[(0,l.jsx)("div",{className:"rounded-md bg-blue-50 border border-blue-200 px-4 py-3 text-sm text-blue-800 mb-4",children:"Your guardrail will be sent for admin review before it becomes active."}),(0,l.jsxs)(u.Form,{form:T,layout:"vertical",initialValues:{mode:"pre_call"},onFinish:async e=>{let t={...e.extra_litellm_params?JSON.parse(e.extra_litellm_params):{},guardrail:"generic_guardrail_api",mode:e.mode,api_base:e.api_base};try{await P.mutateAsync({team_id:e.team_id,guardrail_name:e.guardrail_name,litellm_params:t,guardrail_info:e.guardrail_info?JSON.parse(e.guardrail_info):void 0}),y.default.success("Guardrail submitted for review"),O(!1),T.resetFields(),L()}catch{}},children:[(0,l.jsx)(u.Form.Item,{label:"Team",name:"team_id",rules:[{required:!0,message:"Select a team"}],children:(0,l.jsx)(ay.default,{})}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Name",name:"guardrail_name",rules:[{required:!0,message:"Enter a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"e.g. pii-detection"})}),(0,l.jsx)(u.Form.Item,{label:"Mode",name:"mode",rules:[{required:!0,message:"Select a mode"}],children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"pre_call",children:"Pre Call"}),(0,l.jsx)(x.Select.Option,{value:"post_call",children:"Post Call"}),(0,l.jsx)(x.Select.Option,{value:"during_call",children:"During Call"})]})}),(0,l.jsx)(u.Form.Item,{label:"API Base URL",name:"api_base",rules:[{required:!0,message:"Enter the API base URL"},{type:"url",message:"Must be a valid URL"}],children:(0,l.jsx)(p.Input,{placeholder:"https://your-guardrail-api.com/v1/check",className:"font-mono"})}),(0,l.jsx)(u.Form.Item,{label:"Additional litellm_params (optional)",name:"extra_litellm_params",tooltip:"JSON object merged into litellm_params. e.g. forward_api_key, headers, model, unreachable_fallback",rules:[{validator:(e,t)=>{if(!t)return Promise.resolve();try{let e=JSON.parse(t);if("object"!=typeof e||Array.isArray(e))return Promise.reject("Must be a JSON object");return Promise.resolve()}catch{return Promise.reject("Invalid JSON")}}}],children:(0,l.jsx)(p.Input.TextArea,{rows:3,className:"font-mono text-xs",placeholder:'{"forward_api_key": true, "headers": {"X-Custom": "value"}}'})}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Info (optional)",name:"guardrail_info",rules:[{validator:(e,t)=>{if(!t)return Promise.resolve();try{return JSON.parse(t),Promise.resolve()}catch{return Promise.reject("Invalid JSON")}}}],children:(0,l.jsx)(p.Input.TextArea,{rows:3,className:"font-mono text-xs",placeholder:'{"description": "Detects PII in requests"}'})})]})]})]})}let a$=({accessToken:e,userRole:t})=>{let[a,u]=(0,r.useState)([]),[p,g]=(0,r.useState)(!1),[x,h]=(0,r.useState)(!1),[f,j]=(0,r.useState)(!1),[_,b]=(0,r.useState)(!1),[v,w]=(0,r.useState)(null),[N,C]=(0,r.useState)(!1),[S,k]=(0,r.useState)(null),I=!!t&&(0,tg.isAdminRole)(t),A=async()=>{if(e){j(!0);try{let t=await (0,m.getGuardrailsList)(e);u(t.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{j(!1)}}};(0,r.useEffect)(()=>{A()},[e]);let O=()=>{A()},T=async()=>{if(v&&e){b(!0);try{await (0,m.deleteGuardrailCall)(e,v.guardrail_id),y.default.success(`Guardrail "${v.guardrail_name}" deleted successfully`),await A()}catch(e){console.error("Error deleting guardrail:",e),y.default.fromBackend("Failed to delete guardrail")}finally{b(!1),C(!1),w(null)}}},P=v&&v.litellm_params?eg(v.litellm_params.guardrail).displayName:void 0;return(0,l.jsx)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:(0,l.jsx)(n.Tabs,{defaultActiveKey:"guardrails",items:[...I?[{key:"garden",label:"Guardrail Garden",children:(0,l.jsx)(an,{accessToken:e,onGuardrailCreated:O})},{key:"guardrails",label:"Guardrails",children:(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"flex justify-between items-center mb-4",children:(0,l.jsx)(s.Dropdown,{menu:{items:[{key:"provider",icon:(0,l.jsx)(d.PlusOutlined,{}),label:"Add Provider Guardrail",onClick:()=>{S&&k(null),g(!0)}},{key:"custom_code",icon:(0,l.jsx)(c.CodeOutlined,{}),label:"Create Custom Code Guardrail",onClick:()=>{S&&k(null),h(!0)}}]},trigger:["click"],disabled:!e,children:(0,l.jsxs)(i.Button,{disabled:!e,children:["+ Add New Guardrail ",(0,l.jsx)(o.DownOutlined,{className:"ml-2"})]})})}),S?(0,l.jsx)(tW,{guardrailId:S,onClose:()=>k(null),accessToken:e,isAdmin:I}):(0,l.jsx)(tp,{guardrailsList:a,isLoading:f,onDeleteClick:(e,t)=>{w(a.find(t=>t.guardrail_id===e)||null),C(!0)},accessToken:e,onGuardrailUpdated:A,isAdmin:I,onGuardrailClick:e=>k(e)}),(0,l.jsx)(e1,{visible:p,onClose:()=>{g(!1)},accessToken:e,onSuccess:O}),(0,l.jsx)(tJ,{visible:x,onClose:()=>{h(!1)},accessToken:e,onSuccess:O}),(0,l.jsx)(t3.default,{isOpen:N,title:"Delete Guardrail",message:`Are you sure you want to delete guardrail: ${v?.guardrail_name}? This action cannot be undone.`,resourceInformationTitle:"Guardrail Information",resourceInformation:[{label:"Name",value:v?.guardrail_name},{label:"ID",value:v?.guardrail_id,code:!0},{label:"Provider",value:P},{label:"Mode",value:v?.litellm_params.mode},{label:"Default On",value:v?.litellm_params.default_on?"Yes":"No"}],onCancel:()=>{C(!1),w(null)},onOk:T,confirmLoading:_})]})},{key:"playground",label:"Test Playground",disabled:!e,children:(0,l.jsx)(t6,{guardrailsList:a,isLoading:f,accessToken:e,onClose:()=>{}})}]:[],{key:"submitted",label:"Submitted Guardrails",children:(0,l.jsx)(aF,{accessToken:e})}]})})};e.s(["default",0,function(){let{accessToken:e,userRole:t}=(0,av.default)();return(0,l.jsx)(a$,{accessToken:e,userRole:t})}],509345)}]); \ No newline at end of file + `})]})},t0=({guardrailId:e,onClose:t,accessToken:a,isAdmin:s})=>{let n,[o,d]=(0,r.useState)(null),[g,h]=(0,r.useState)(null),[f,j]=(0,r.useState)(!0),[_,b]=(0,r.useState)(!1),[v]=u.Form.useForm(),[w,C]=(0,r.useState)([]),[N,k]=(0,r.useState)({}),[S,I]=(0,r.useState)(null),[A,O]=(0,r.useState)({}),[T,P]=(0,r.useState)(!1),L={rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""},[B,F]=(0,r.useState)(L),[$,E]=(0,r.useState)(!1),[M,R]=(0,r.useState)(!1),G=r.default.useRef({patterns:[],blockedWords:[],categories:[]}),z=(0,r.useCallback)((e,t,a,l,r)=>{G.current={patterns:e,blockedWords:t,categories:a||[],competitorIntentEnabled:l,competitorIntentConfig:r}},[]),D=async()=>{try{if(j(!0),!a)return;let t=await (0,m.getGuardrailInfo)(a,e);if(d(t),t.litellm_params?.pii_entities_config){let e=t.litellm_params.pii_entities_config;if(C([]),k({}),Object.keys(e).length>0){let t=[],a={};Object.entries(e).forEach(([e,l])=>{t.push(e),a[e]="string"==typeof l?l:"MASK"}),C(t),k(a)}}else C([]),k({})}catch(e){y.default.fromBackend("Failed to load guardrail information"),console.error("Error fetching guardrail info:",e)}finally{j(!1)}},K=async()=>{try{if(!a)return;let e=await (0,m.getGuardrailProviderSpecificParams)(a);h(e)}catch(e){console.error("Error fetching guardrail provider specific params:",e)}},q=async()=>{try{if(!a)return;let e=await (0,m.getGuardrailUISettings)(a);I(e)}catch(e){console.error("Error fetching guardrail UI settings:",e)}};(0,r.useEffect)(()=>{K()},[a]),(0,r.useEffect)(()=>{D(),q()},[e,a]),(0,r.useEffect)(()=>{if(o&&v){let e={...o.litellm_params||{}};delete e.skip_system_message_in_guardrail,delete e.skip_tool_message_in_guardrail,v.setFieldsValue({guardrail_name:o.guardrail_name,...e,skip_system_message_choice:ef(o.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:ey(o.litellm_params?.skip_tool_message_in_guardrail),guardrail_info:o.guardrail_info?JSON.stringify(o.guardrail_info,null,2):"",...o.litellm_params?.optional_params&&{optional_params:o.litellm_params.optional_params}})}},[o,g,v]);let H=(0,r.useCallback)(()=>{o?.litellm_params?.guardrail==="tool_permission"?F({rules:o.litellm_params?.rules||[],default_action:(o.litellm_params?.default_action||"deny").toLowerCase(),on_disallowed_action:(o.litellm_params?.on_disallowed_action||"block").toLowerCase(),violation_message_template:o.litellm_params?.violation_message_template||""}):F(L),E(!1)},[o]);(0,r.useEffect)(()=>{H()},[H]);let U=async t=>{try{if(!a)return;let d={litellm_params:{}};t.guardrail_name!==o.guardrail_name&&(d.guardrail_name=t.guardrail_name),t.default_on!==o.litellm_params?.default_on&&(d.litellm_params.default_on=t.default_on);let c=ef(o.litellm_params?.skip_system_message_in_guardrail),u=t.skip_system_message_choice;void 0!==u&&u!==c&&("inherit"===u?d.litellm_params.skip_system_message_in_guardrail=null:"yes"===u?d.litellm_params.skip_system_message_in_guardrail=!0:d.litellm_params.skip_system_message_in_guardrail=!1);let p=ey(o.litellm_params?.skip_tool_message_in_guardrail),x=t.skip_tool_message_choice;void 0!==x&&x!==p&&("inherit"===x?d.litellm_params.skip_tool_message_in_guardrail=null:"yes"===x?d.litellm_params.skip_tool_message_in_guardrail=!0:d.litellm_params.skip_tool_message_in_guardrail=!1);let h=o.guardrail_info,f=t.guardrail_info?JSON.parse(t.guardrail_info):void 0;JSON.stringify(h)!==JSON.stringify(f)&&(d.guardrail_info=f);let j=o.litellm_params?.pii_entities_config||{},_={};if(w.forEach(e=>{_[e]=N[e]||"MASK"}),JSON.stringify(j)!==JSON.stringify(_)&&(d.litellm_params.pii_entities_config=_),o.litellm_params?.guardrail==="litellm_content_filter"&&T){var l,r,i,s,n;let e,t=(l=G.current.patterns||[],r=G.current.blockedWords||[],i=G.current.categories||[],s=G.current.competitorIntentEnabled,n=G.current.competitorIntentConfig,e={patterns:l.map(e=>({pattern_type:"prebuilt"===e.type?"prebuilt":"regex",pattern_name:"prebuilt"===e.type?e.name:void 0,pattern:"custom"===e.type?e.pattern:void 0,name:e.name,action:e.action})),blocked_words:r.map(e=>({keyword:e.keyword,action:e.action,description:e.description}))},void 0!==i&&(e.categories=i.map(e=>({category:e.category,enabled:!0,action:e.action,severity_threshold:e.severity_threshold||"medium"}))),s&&n&&n.brand_self.length>0&&(e.competitor_intent_config={competitor_intent_type:n.competitor_intent_type,brand_self:n.brand_self,locations:n.locations?.length?n.locations:void 0,competitors:"generic"===n.competitor_intent_type&&n.competitors?.length?n.competitors:void 0,policy:n.policy,threshold_high:n.threshold_high,threshold_medium:n.threshold_medium,threshold_low:n.threshold_low}),e);d.litellm_params.patterns=t.patterns,d.litellm_params.blocked_words=t.blocked_words,d.litellm_params.categories=t.categories,d.litellm_params.competitor_intent_config=t.competitor_intent_config??null}if(o.litellm_params?.guardrail==="tool_permission"){let e=o.litellm_params?.rules||[],t=B.rules||[],a=JSON.stringify(e)!==JSON.stringify(t),l=(o.litellm_params?.default_action||"deny").toLowerCase(),r=(B.default_action||"deny").toLowerCase(),i=l!==r,s=(o.litellm_params?.on_disallowed_action||"block").toLowerCase(),n=(B.on_disallowed_action||"block").toLowerCase(),c=s!==n,m=o.litellm_params?.violation_message_template||"",u=B.violation_message_template||"",p=m!==u;($||a||i||c||p)&&(d.litellm_params.rules=t,d.litellm_params.default_action=r,d.litellm_params.on_disallowed_action=n,d.litellm_params.violation_message_template=u||null)}let v=Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail),C=o.litellm_params?.guardrail==="tool_permission";if(g&&v&&!C){let e=g[en[v]?.toLowerCase()]||{},a=new Set;Object.keys(e).forEach(e=>{"optional_params"!==e&&a.add(e)}),e.optional_params&&e.optional_params.fields&&Object.keys(e.optional_params.fields).forEach(e=>{a.add(e)}),a.forEach(e=>{if("patterns"===e||"blocked_words"===e||"categories"===e)return;let a=t[e];(null==a||""===a)&&(a=t.optional_params?.[e]);let l=o.litellm_params?.[e];JSON.stringify(a)!==JSON.stringify(l)&&(null!=a&&""!==a?d.litellm_params[e]=a:null!=l&&""!==l&&(d.litellm_params[e]=null))})}if(0===Object.keys(d.litellm_params).length&&delete d.litellm_params,0===Object.keys(d).length){y.default.info("No changes detected"),b(!1);return}await (0,m.updateGuardrailCall)(a,e,d),y.default.success("Guardrail updated successfully"),P(!1),D(),b(!1)}catch(e){console.error("Error updating guardrail:",e),y.default.fromBackend("Failed to update guardrail")}};if(f)return(0,l.jsx)("div",{className:"p-4",children:"Loading..."});if(!o)return(0,l.jsx)("div",{className:"p-4",children:"Guardrail not found"});let J=e=>e?new Date(e).toLocaleString():"-",{logo:W,displayName:V}=eh(o.litellm_params?.guardrail||""),Y=async(e,t)=>{await (0,t_.copyToClipboard)(e)&&(O(e=>({...e,[t]:!0})),setTimeout(()=>{O(e=>({...e,[t]:!1}))},2e3))},Q="config"===o.guardrail_definition_location;return(0,l.jsxs)("div",{className:"p-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(i.Button,{type:"text",icon:(0,l.jsx)(tb.ArrowLeftIcon,{className:"w-4 h-4"}),onClick:t,className:"mb-4",children:"Back to Guardrails"}),(0,l.jsx)(tA.Title,{children:o.guardrail_name||"Unnamed Guardrail"}),(0,l.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,l.jsx)(eU.Text,{className:"text-gray-500 font-mono",children:o.guardrail_id}),(0,l.jsx)(i.Button,{type:"text",size:"small",icon:A["guardrail-id"]?(0,l.jsx)(tO.CheckIcon,{size:12}):(0,l.jsx)(tT.CopyIcon,{size:12}),onClick:()=>Y(o.guardrail_id,"guardrail-id"),className:`left-2 z-10 transition-all duration-200 ${A["guardrail-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]}),(0,l.jsxs)(tN.TabGroup,{children:[(0,l.jsxs)(tk.TabList,{className:"mb-4",children:[(0,l.jsx)(tC.Tab,{children:"Overview"},"overview"),s?(0,l.jsx)(tC.Tab,{children:"Settings"},"settings"):(0,l.jsx)(l.Fragment,{})]}),(0,l.jsxs)(tI.TabPanels,{children:[(0,l.jsxs)(tS.TabPanel,{children:[(0,l.jsxs)(tw.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,l.jsxs)(eH.Card,{children:[(0,l.jsx)(eU.Text,{children:"Provider"}),(0,l.jsxs)("div",{className:"mt-2 flex items-center space-x-2",children:[W&&(0,l.jsx)("img",{src:W,alt:`${V} logo`,className:"w-6 h-6",onError:e=>{e.target.style.display="none"}}),(0,l.jsx)(tA.Title,{children:V})]})]}),(0,l.jsxs)(eH.Card,{children:[(0,l.jsx)(eU.Text,{children:"Mode"}),(0,l.jsxs)("div",{className:"mt-2",children:[(0,l.jsx)(tA.Title,{children:o.litellm_params?.mode||"-"}),(0,l.jsx)(tv.Badge,{color:o.litellm_params?.default_on?"green":"gray",children:o.litellm_params?.default_on?"Default On":"Default Off"})]})]}),(0,l.jsxs)(eH.Card,{children:[(0,l.jsx)(eU.Text,{children:"Created At"}),(0,l.jsxs)("div",{className:"mt-2",children:[(0,l.jsx)(tA.Title,{children:J(o.created_at)}),(0,l.jsxs)(eU.Text,{children:["Last Updated: ",J(o.updated_at)]})]})]})]}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsx)(eH.Card,{className:"mt-6",children:(0,l.jsxs)("div",{className:"flex justify-between items-center",children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"PII Protection"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[Object.keys(o.litellm_params.pii_entities_config).length," PII entities configured"]})]})}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsx)(eU.Text,{className:"mb-4 text-lg font-semibold",children:"PII Entity Configuration"}),(0,l.jsxs)("div",{className:"border rounded-lg overflow-hidden shadow-xs",children:[(0,l.jsxs)("div",{className:"bg-gray-50 px-5 py-3 border-b flex",children:[(0,l.jsx)(eU.Text,{className:"flex-1 font-semibold text-gray-700",children:"Entity Type"}),(0,l.jsx)(eU.Text,{className:"flex-1 font-semibold text-gray-700",children:"Configuration"})]}),(0,l.jsx)("div",{className:"max-h-[400px] overflow-y-auto",children:Object.entries(o.litellm_params?.pii_entities_config).map(([e,t])=>(0,l.jsxs)("div",{className:"px-5 py-3 flex border-b hover:bg-gray-50 transition-colors",children:[(0,l.jsx)(eU.Text,{className:"flex-1 font-medium text-gray-900",children:e}),(0,l.jsx)(eU.Text,{className:"flex-1",children:(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 ${"MASK"===t?"text-blue-600":"text-red-600"}`,children:["MASK"===t?(0,l.jsx)(eT.default,{}):(0,l.jsx)(eP.StopOutlined,{}),String(t)]})})]},e))})]})]}),o.litellm_params?.guardrail==="tool_permission"&&(0,l.jsx)(eH.Card,{className:"mt-6",children:(0,l.jsx)(eQ,{value:B,disabled:!0})}),o.litellm_params?.guardrail==="custom_code"&&o.litellm_params?.custom_code&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)(c.CodeOutlined,{className:"text-blue-500"}),(0,l.jsx)(eU.Text,{className:"font-medium text-lg",children:"Custom Code"})]}),s&&!Q&&(0,l.jsx)(i.Button,{size:"small",icon:(0,l.jsx)(c.CodeOutlined,{}),onClick:()=>R(!0),children:"Edit Code"})]}),(0,l.jsx)("div",{className:"relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e]",children:(0,l.jsx)("pre",{className:"p-4 text-sm text-gray-200 overflow-x-auto",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace"},children:(0,l.jsx)("code",{children:o.litellm_params.custom_code})})})]}),(0,l.jsx)(tM,{guardrailData:o,guardrailSettings:S,isEditing:!1,accessToken:a})]}),s&&(0,l.jsx)(tS.TabPanel,{children:(0,l.jsxs)(eH.Card,{children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(tA.Title,{children:"Guardrail Settings"}),Q&&(0,l.jsx)(ek.Tooltip,{title:"Guardrail is defined in the config file and cannot be edited.",children:(0,l.jsx)(eV.InfoCircleOutlined,{})}),!_&&!Q&&(o.litellm_params?.guardrail==="custom_code"?(0,l.jsx)(i.Button,{icon:(0,l.jsx)(c.CodeOutlined,{}),onClick:()=>R(!0),children:"Edit Code"}):(0,l.jsx)(i.Button,{onClick:()=>b(!0),children:"Edit Settings"}))]}),_?(0,l.jsxs)(u.Form,{form:v,onFinish:U,initialValues:{guardrail_name:o.guardrail_name,...(n={...o.litellm_params||{}},delete n.skip_system_message_in_guardrail,delete n.skip_tool_message_in_guardrail,n),skip_system_message_choice:ef(o.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:ey(o.litellm_params?.skip_tool_message_in_guardrail),guardrail_info:o.guardrail_info?JSON.stringify(o.guardrail_info,null,2):"",...o.litellm_params?.optional_params&&{optional_params:o.litellm_params.optional_params}},layout:"vertical",children:[(0,l.jsx)(u.Form.Item,{label:"Guardrail Name",name:"guardrail_name",rules:[{required:!0,message:"Please input a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"Enter guardrail name"})}),(0,l.jsx)(u.Form.Item,{label:"Default On",name:"default_on",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"Yes"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"No"})]})}),(0,l.jsx)(u.Form.Item,{label:"Skip system messages in guardrail",name:"skip_system_message_choice",tooltip:"Unified guardrails: omit role: system from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{label:"Skip tool messages in guardrail",name:"skip_tool_message_choice",tooltip:"Unified guardrails: omit role: tool from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),o.litellm_params?.guardrail==="presidio"&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eJ.Divider,{orientation:"left",children:"PII Protection"}),(0,l.jsx)("div",{className:"mb-6",children:S&&(0,l.jsx)(eq,{entities:S.supported_entities,actions:S.supported_actions,selectedEntities:w,selectedActions:N,onEntitySelect:e=>{C(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},onActionSelect:(e,t)=>{k(a=>({...a,[e]:t}))},entityCategories:S.pii_entity_categories})})]}),(0,l.jsx)(tM,{guardrailData:o,guardrailSettings:S,isEditing:!0,accessToken:a,onDataChange:z,onUnsavedChanges:P}),(o.litellm_params?.guardrail==="tool_permission"||g)&&(0,l.jsx)(eJ.Divider,{orientation:"left",children:"Provider Settings"}),o.litellm_params?.guardrail==="tool_permission"?(0,l.jsx)(eQ,{value:B,onChange:F}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eN,{selectedProvider:Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail)||null,accessToken:a,providerParams:g,value:o.litellm_params}),g&&(()=>{let e=Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail);if(!e)return null;let t=g[en[e]?.toLowerCase()];return t&&t.optional_params?(0,l.jsx)(ev,{optionalParams:t.optional_params,parentFieldKey:"optional_params",values:o.litellm_params}):null})()]}),(0,l.jsx)(eJ.Divider,{orientation:"left",children:"Advanced Settings"}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Information",name:"guardrail_info",children:(0,l.jsx)(p.Input.TextArea,{rows:5})}),(0,l.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,l.jsx)(i.Button,{onClick:()=>{b(!1),P(!1),H()},children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",htmlType:"submit",children:"Save Changes"})]})]}):(0,l.jsxs)("div",{className:"space-y-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Guardrail ID"}),(0,l.jsx)("div",{className:"font-mono",children:o.guardrail_id})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Guardrail Name"}),(0,l.jsx)("div",{children:o.guardrail_name||"Unnamed Guardrail"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Provider"}),(0,l.jsx)("div",{children:V})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Mode"}),(0,l.jsx)("div",{children:o.litellm_params?.mode||"-"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Default On"}),(0,l.jsx)(tv.Badge,{color:o.litellm_params?.default_on?"green":"gray",children:o.litellm_params?.default_on?"Yes":"No"})]}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"PII Protection"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsxs)(tv.Badge,{color:"blue",children:[Object.keys(o.litellm_params.pii_entities_config).length," PII entities configured"]})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Created At"}),(0,l.jsx)("div",{children:J(o.created_at)})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Last Updated"}),(0,l.jsx)("div",{children:J(o.updated_at)})]}),o.litellm_params?.guardrail==="tool_permission"&&(0,l.jsx)(eQ,{value:B,disabled:!0})]})]})})]})]}),(0,l.jsx)(tZ,{visible:M,onClose:()=>R(!1),onSuccess:()=>{R(!1),D()},accessToken:a,editData:o?{guardrail_id:o.guardrail_id,guardrail_name:o.guardrail_name,litellm_params:o.litellm_params}:null})]})};var t1=e.i(573421),t2=e.i(19732),t4=e.i(928685),t5=e.i(166406),t8=e.i(637235),t6=e.i(240647);let{Text:t3}=f.Typography,t7=function({results:e,errors:t}){let[a,i]=(0,r.useState)(new Set),s=e=>{let t=new Set(a);t.has(e)?t.delete(e):t.add(e),i(t)},n=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0;{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.focus(),t.select();let a=document.execCommand("copy");if(document.body.removeChild(t),!a)throw Error("execCommand failed");return!0}}catch(e){return console.error("Copy failed:",e),!1}};return e||t?(0,l.jsxs)("div",{className:"space-y-3 pt-4 border-t border-gray-200",children:[(0,l.jsx)("h3",{className:"text-sm font-semibold text-gray-900",children:"Results"}),e&&e.map(e=>{let t=a.has(e.guardrailName);return(0,l.jsx)(eH.Card,{className:"bg-green-50 border-green-200",children:(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-2 cursor-pointer flex-1",onClick:()=>s(e.guardrailName),children:[t?(0,l.jsx)(t6.RightOutlined,{className:"text-gray-500 text-xs"}):(0,l.jsx)(o.DownOutlined,{className:"text-gray-500 text-xs"}),(0,l.jsx)(tG.CheckCircleOutlined,{className:"text-green-600 text-lg"}),(0,l.jsx)("span",{className:"text-sm font-medium text-green-800",children:e.guardrailName})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-600",children:[(0,l.jsx)(t8.ClockCircleOutlined,{}),(0,l.jsxs)("span",{className:"font-medium",children:[e.latency,"ms"]})]}),!t&&(0,l.jsx)(tm.Button,{size:"xs",variant:"secondary",icon:t5.CopyOutlined,onClick:async()=>{await n(e.response_text)?y.default.success("Result copied to clipboard"):y.default.fromBackend("Failed to copy result")},children:"Copy"})]})]}),!t&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"bg-white border border-green-200 rounded-sm p-3",children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-600 mb-2 block",children:"Output Text"}),(0,l.jsx)("div",{className:"font-mono text-sm text-gray-900 whitespace-pre-wrap wrap-break-word",children:e.response_text})]}),(0,l.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,l.jsx)("span",{className:"font-medium",children:"Characters:"})," ",e.response_text.length]})]})]})},e.guardrailName)}),t&&t.map(e=>{let t=a.has(e.guardrailName);return(0,l.jsx)(eH.Card,{className:"bg-red-50 border-red-200",children:(0,l.jsxs)("div",{className:"flex items-start space-x-2",children:[(0,l.jsx)("div",{className:"cursor-pointer mt-0.5",onClick:()=>s(e.guardrailName),children:t?(0,l.jsx)(t6.RightOutlined,{className:"text-gray-500 text-xs"}):(0,l.jsx)(o.DownOutlined,{className:"text-gray-500 text-xs"})}),(0,l.jsx)("div",{className:"text-red-600 mt-0.5",children:(0,l.jsx)("svg",{className:"w-5 h-5",fill:"currentColor",viewBox:"0 0 20 20",children:(0,l.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",clipRule:"evenodd"})})}),(0,l.jsxs)("div",{className:"flex-1",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,l.jsxs)("p",{className:"text-sm font-medium text-red-800 cursor-pointer",onClick:()=>s(e.guardrailName),children:[e.guardrailName," - Error"]}),(0,l.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-600",children:[(0,l.jsx)(t8.ClockCircleOutlined,{}),(0,l.jsxs)("span",{className:"font-medium",children:[e.latency,"ms"]})]})]}),!t&&(0,l.jsx)("p",{className:"text-sm text-red-700 mt-1",children:e.error.message})]})]})},e.guardrailName)})]}):null},{TextArea:t9}=p.Input,{Text:ae}=f.Typography,at=function({guardrailNames:e,onSubmit:t,isLoading:a,results:i,errors:s,onClose:n}){let[o,d]=(0,r.useState)(""),c=()=>{o.trim()?t(o):y.default.fromBackend("Please enter text to test")},m=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0;{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.focus(),t.select();let a=document.execCommand("copy");if(document.body.removeChild(t),!a)throw Error("execCommand failed");return!0}}catch(e){return console.error("Copy failed:",e),!1}},u=async()=>{await m(o)?y.default.success("Input copied to clipboard"):y.default.fromBackend("Failed to copy input")};return(0,l.jsxs)("div",{className:"space-y-4 h-full flex flex-col",children:[(0,l.jsx)("div",{className:"flex items-center justify-between pb-3 border-b border-gray-200",children:(0,l.jsx)("div",{className:"flex items-center space-x-3",children:(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-2 mb-1",children:[(0,l.jsx)("h2",{className:"text-lg font-semibold text-gray-900",children:"Test Guardrails:"}),(0,l.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map(e=>(0,l.jsx)("div",{className:"inline-flex items-center space-x-1 bg-blue-50 px-3 py-1 rounded-md border border-blue-200",children:(0,l.jsx)("span",{className:"font-mono text-blue-700 font-medium text-sm",children:e})},e))})]}),(0,l.jsxs)("p",{className:"text-sm text-gray-500",children:["Test ",e.length>1?"guardrails":"guardrail"," and compare results"]})]})})}),(0,l.jsxs)("div",{className:"flex-1 overflow-auto space-y-4",children:[(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-2",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Input Text"}),(0,l.jsx)(ek.Tooltip,{title:"Press Enter to submit. Use Shift+Enter for new line.",children:(0,l.jsx)(eV.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),o&&(0,l.jsx)(tm.Button,{size:"xs",variant:"secondary",icon:t5.CopyOutlined,onClick:u,children:"Copy Input"})]}),(0,l.jsx)(t9,{value:o,onChange:e=>d(e.target.value),onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||e.ctrlKey||e.metaKey||(e.preventDefault(),c())},placeholder:"Enter text to test with guardrails...",rows:8,className:"font-mono text-sm"}),(0,l.jsxs)("div",{className:"flex justify-between items-center mt-1",children:[(0,l.jsxs)(ae,{className:"text-xs text-gray-500",children:["Press ",(0,l.jsx)("kbd",{className:"px-1 py-0.5 bg-gray-100 border border-gray-300 rounded-sm text-xs",children:"Enter"})," to submit •"," ",(0,l.jsx)("kbd",{className:"px-1 py-0.5 bg-gray-100 border border-gray-300 rounded-sm text-xs",children:"Shift+Enter"})," for new line"]}),(0,l.jsxs)(ae,{className:"text-xs text-gray-500",children:["Characters: ",o.length]})]})]}),(0,l.jsx)("div",{className:"pt-2",children:(0,l.jsx)(tm.Button,{onClick:c,loading:a,disabled:!o.trim(),className:"w-full",children:a?`Testing ${e.length} guardrail${e.length>1?"s":""}...`:`Test ${e.length} guardrail${e.length>1?"s":""}`})})]}),(0,l.jsx)(t7,{results:i,errors:s})]})]})},aa=({guardrailsList:e,isLoading:t,accessToken:a,onClose:i})=>{let[s,n]=(0,r.useState)(new Set),[o,d]=(0,r.useState)(""),[c,u]=(0,r.useState)([]),[g,x]=(0,r.useState)([]),[h,j]=(0,r.useState)(!1),_=e.filter(e=>e.guardrail_name?.toLowerCase().includes(o.toLowerCase())),v=async e=>{if(0===s.size||!a)return;j(!0),u([]),x([]);let t=[],l=[];await Promise.all(Array.from(s).map(async r=>{let i=Date.now();try{let l=await (0,m.applyGuardrail)(a,r,e,null,null),s=Date.now()-i;t.push({guardrailName:r,response_text:l.response_text,latency:s})}catch(t){let e=Date.now()-i;console.error(`Error testing guardrail ${r}:`,t),l.push({guardrailName:r,error:t,latency:e})}})),u(t),x(l),j(!1),t.length>0&&y.default.success(`${t.length} guardrail${t.length>1?"s":""} applied successfully`),l.length>0&&y.default.fromBackend(`${l.length} guardrail${l.length>1?"s":""} failed`)};return(0,l.jsx)("div",{className:"w-full h-[calc(100vh-200px)]",children:(0,l.jsx)(b.Card,{className:"h-full",styles:{body:{padding:0,height:"100%"}},children:(0,l.jsxs)("div",{className:"flex h-full",children:[(0,l.jsxs)("div",{className:"w-1/4 border-r border-gray-200 flex flex-col overflow-hidden",children:[(0,l.jsx)("div",{className:"p-4 border-b border-gray-200",children:(0,l.jsxs)("div",{className:"mb-3",children:[(0,l.jsx)("h3",{className:"text-lg font-semibold mb-3",children:"Guardrails"}),(0,l.jsx)(p.Input,{prefix:(0,l.jsx)(t4.SearchOutlined,{}),placeholder:"Search guardrails...",value:o,onChange:e=>d(e.target.value)})]})}),(0,l.jsx)("div",{className:"flex-1 overflow-auto",children:t?(0,l.jsx)("div",{className:"flex items-center justify-center h-32",children:(0,l.jsx)(ew.Spin,{})}):0===_.length?(0,l.jsx)("div",{className:"p-4",children:(0,l.jsx)(eW.Empty,{description:o?"No guardrails match your search":"No guardrails available"})}):(0,l.jsx)(t1.List,{dataSource:_,renderItem:e=>(0,l.jsx)(t1.List.Item,{onClick:()=>{var t;let a;e.guardrail_name&&(t=e.guardrail_name,(a=new Set(s)).has(t)?a.delete(t):a.add(t),n(a))},style:{paddingLeft:24,paddingRight:16},className:`cursor-pointer hover:bg-gray-50 transition-colors ${s.has(e.guardrail_name||"")?"bg-blue-50 border-l-4 border-l-blue-500":"border-l-4 border-l-transparent"}`,children:(0,l.jsx)(t1.List.Item.Meta,{title:(0,l.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,l.jsx)(t2.ExperimentOutlined,{className:"text-gray-400"}),(0,l.jsx)("span",{className:"font-medium text-gray-900",children:e.guardrail_name})]}),description:(0,l.jsxs)("div",{className:"text-xs space-y-1 mt-1",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-medium",children:"Type: "}),(0,l.jsx)("span",{className:"text-gray-600",children:e.litellm_params.guardrail})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-medium",children:"Mode: "}),(0,l.jsx)("span",{className:"text-gray-600",children:e.litellm_params.mode})]})]})})})})}),(0,l.jsx)("div",{className:"p-3 border-t border-gray-200 bg-gray-50",children:(0,l.jsxs)(f.Typography.Text,{className:"text-xs text-gray-600",children:[s.size," of ",_.length," selected"]})})]}),(0,l.jsxs)("div",{className:"w-3/4 flex flex-col bg-white",children:[(0,l.jsx)("div",{className:"p-4 border-b border-gray-200 flex justify-between items-center",children:(0,l.jsx)(f.Typography.Title,{level:2,className:"text-xl font-semibold mb-0",children:"Guardrail Testing Playground"})}),(0,l.jsx)("div",{className:"flex-1 overflow-auto p-4",children:0===s.size?(0,l.jsxs)("div",{className:"h-full flex flex-col items-center justify-center text-gray-400",children:[(0,l.jsx)(t2.ExperimentOutlined,{style:{fontSize:"48px",marginBottom:"16px"}}),(0,l.jsx)(f.Typography.Paragraph,{className:"text-lg font-medium text-gray-600 mb-2",children:"Select Guardrails to Test"}),(0,l.jsx)(f.Typography.Paragraph,{className:"text-center text-gray-500 max-w-md",children:"Choose one or more guardrails from the left sidebar to start testing and comparing results."})]}):(0,l.jsx)("div",{className:"h-full",children:(0,l.jsx)(at,{guardrailNames:Array.from(s),onSubmit:v,results:c.length>0?c:null,errors:g.length>0?g:null,isLoading:h,onClose:()=>n(new Set)})})})]})]})})})};var al=e.i(127952),ar=e.i(266537);let ai="/ui/assets/logos/",as=[{id:"cf_denied_financial",name:"Denied Financial Advice",description:"Detects requests for personalized financial advice, investment recommendations, or financial planning.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"],eval:{f1:100,precision:100,recall:100,testCases:207,latency:"<0.1ms"}},{id:"cf_denied_insults",name:"Insults & Personal Attacks",description:"Detects insults, name-calling, and personal attacks directed at the chatbot, staff, or other people.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"],eval:{f1:100,precision:100,recall:100,testCases:299,latency:"<0.1ms"}},{id:"cf_denied_legal",name:"Denied Legal Advice",description:"Detects requests for unauthorized legal advice, case analysis, or legal recommendations.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"]},{id:"cf_denied_medical",name:"Denied Medical Advice",description:"Detects requests for medical diagnosis, treatment recommendations, or health advice.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"]},{id:"cf_harmful_violence",name:"Harmful Violence",description:"Detects content related to violence, criminal planning, attacks, and violent threats.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_self_harm",name:"Harmful Self-Harm",description:"Detects content related to self-harm, suicide, and dangerous self-destructive behavior.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_child_safety",name:"Harmful Child Safety",description:"Detects content that could endanger child safety or exploit minors.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_illegal_weapons",name:"Harmful Illegal Weapons",description:"Detects content related to illegal weapons manufacturing, distribution, or acquisition.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_bias_gender",name:"Bias: Gender",description:"Detects gender-based discrimination, stereotypes, and biased language.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_racial",name:"Bias: Racial",description:"Detects racial discrimination, stereotypes, and racially biased content.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_religious",name:"Bias: Religious",description:"Detects religious discrimination, intolerance, and religiously biased content.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_sexual_orientation",name:"Bias: Sexual Orientation",description:"Detects discrimination based on sexual orientation and related biased content.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_prompt_injection_jailbreak",name:"Prompt Injection: Jailbreak",description:"Detects jailbreak attempts designed to bypass AI safety guidelines and restrictions.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_data_exfil",name:"Prompt Injection: Data Exfiltration",description:"Detects attempts to extract sensitive data through prompt manipulation.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_sql",name:"Prompt Injection: SQL",description:"Detects SQL injection attempts embedded in prompts.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_malicious_code",name:"Prompt Injection: Malicious Code",description:"Detects attempts to inject malicious code through prompts.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_system_prompt",name:"Prompt Injection: System Prompt",description:"Detects attempts to extract or override system prompts.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_toxic_abuse",name:"Toxic & Abusive Language",description:"Detects toxic, abusive, and hateful language across multiple languages (EN, AU, DE, ES, FR).",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Toxicity"]},{id:"cf_patterns",name:"Pattern Matching",description:"Detect and block sensitive data patterns like SSNs, credit card numbers, API keys, and custom regex patterns.",category:"litellm",subcategory:"Patterns",logo:`${ai}litellm_logo.jpg`,tags:["PII","Regex","Data Protection"]},{id:"cf_keywords",name:"Keyword Blocking",description:"Block or mask content containing specific keywords or phrases. Upload custom word lists or add individual terms.",category:"litellm",subcategory:"Keywords",logo:`${ai}litellm_logo.jpg`,tags:["Keywords","Blocklist"]},{id:"block_code_execution",name:"Block Code Execution",description:"Detects markdown fenced code blocks in requests and responses. Block or mask executable code (e.g. Python, JavaScript, Bash) by language with configurable confidence.",category:"litellm",subcategory:"Code Safety",logo:`${ai}litellm_logo.jpg`,tags:["Code","Safety","Prompt Injection"]},{id:"cf_competitor_intent",name:"Competitor Name Blocking",description:"Block or reframe competitor comparison and ranking intent. Detect when users ask to compare or recommend competitors (airline or generic competitor lists).",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Competitor","Topic Blocker"]},{id:"presidio",name:"Presidio PII",description:"Microsoft Presidio for PII detection and anonymization. Supports 30+ entity types with configurable actions.",category:"partner",logo:`${ai}microsoft_azure.svg`,tags:["PII","Microsoft"],providerKey:"PresidioPII"},{id:"bedrock",name:"Bedrock Guardrail",description:"AWS Bedrock Guardrails for content filtering, topic avoidance, and sensitive information detection.",category:"partner",logo:`${ai}bedrock.svg`,tags:["AWS","Content Safety"],providerKey:"Bedrock"},{id:"lakera",name:"Lakera",description:"AI security platform protecting against prompt injections, data leakage, and harmful content.",category:"partner",logo:`${ai}lakeraai.jpeg`,tags:["Security","Prompt Injection"],providerKey:"Lakera"},{id:"openai_moderation",name:"OpenAI Moderation",description:"OpenAI's content moderation API for detecting harmful content across multiple categories.",category:"partner",logo:`${ai}openai_small.svg`,tags:["Content Moderation","OpenAI"]},{id:"google_model_armor",name:"Google Cloud Model Armor",description:"Google Cloud's model protection service for safe and responsible AI deployments.",category:"partner",logo:`${ai}google.svg`,tags:["Google Cloud","Safety"]},{id:"guardrails_ai",name:"Guardrails AI",description:"Open-source framework for adding structural, type, and quality guarantees to LLM outputs.",category:"partner",logo:`${ai}guardrails_ai.jpeg`,tags:["Open Source","Validation"]},{id:"zscaler",name:"Zscaler AI Guard",description:"Enterprise AI security from Zscaler for monitoring and protecting AI/ML workloads.",category:"partner",logo:`${ai}zscaler.svg`,tags:["Enterprise","Security"]},{id:"panw",name:"PANW Prisma AIRS",description:"Palo Alto Networks Prisma AI Runtime Security for securing AI applications in production.",category:"partner",logo:`${ai}palo_alto_networks.jpeg`,tags:["Enterprise","Security"]},{id:"cisco_ai_defense",name:"Cisco AI Defense",description:"Cisco AI Defense Inspection API for runtime protection: prompt injection, PII/PCI/PHI, harassment, hate speech, profanity, violence, and code detection.",category:"partner",logo:`${ai}cisco.png`,tags:["Enterprise","Security","Prompt Injection","PII"],providerKey:"CiscoAiDefense"},{id:"noma",name:"Noma Security",description:"AI security platform for detecting and preventing AI-specific threats and vulnerabilities.",category:"partner",logo:`${ai}noma_security.png`,tags:["Security","Threat Detection"]},{id:"aporia",name:"Aporia AI",description:"Real-time AI guardrails for hallucination detection, topic control, and policy enforcement.",category:"partner",logo:`${ai}aporia.png`,tags:["Hallucination","Policy"]},{id:"aim",name:"AIM Guardrail",description:"AIM Security guardrails for comprehensive AI threat detection and mitigation.",category:"partner",logo:`${ai}aim_security.jpeg`,tags:["Security","Threat Detection"]},{id:"cato_networks",name:"Cato Networks Guardrail",description:"Cato Networks guardrails for comprehensive AI threat detection and mitigation.",category:"partner",logo:`${ai}cato_networks.svg`,tags:["Security","Threat Detection"]},{id:"prompt_security",name:"Prompt Security",description:"Protect against prompt injection attacks, data leakage, and other LLM security threats.",category:"partner",logo:`${ai}prompt_security.png`,tags:["Prompt Injection","Security"]},{id:"lasso",name:"Lasso Guardrail",description:"Content moderation and safety guardrails for responsible AI deployments.",category:"partner",logo:`${ai}lasso.png`,tags:["Content Moderation"]},{id:"pangea",name:"Pangea Guardrail",description:"Pangea's AI guardrails for secure, compliant, and trustworthy AI applications.",category:"partner",logo:`${ai}pangea.png`,tags:["Compliance","Security"]},{id:"enkryptai",name:"EnkryptAI",description:"AI security and governance platform for enterprise AI safety and compliance.",category:"partner",logo:`${ai}enkrypt_ai.avif`,tags:["Enterprise","Governance"]},{id:"javelin",name:"Javelin Guardrails",description:"AI gateway with built-in guardrails for secure and compliant AI operations.",category:"partner",logo:`${ai}javelin.png`,tags:["Gateway","Security"]},{id:"pillar",name:"Pillar Guardrail",description:"AI safety platform for monitoring, testing, and securing AI systems.",category:"partner",logo:`${ai}pillar.jpeg`,tags:["Monitoring","Safety"]},{id:"akto",name:"Akto Guardrail",description:"AI security platform from Akto.io with automatic monitoring and guardrails for AI/ML applications.",category:"partner",logo:`${ai}akto.svg`,tags:["Security","Safety","Monitoring"]},{id:"promptguard",name:"PromptGuard",description:"AI security gateway with prompt injection detection, PII redaction, topic filtering, entity blocklists, and hallucination detection. Self-hostable with drop-in proxy integration.",category:"partner",logo:`${ai}promptguard.svg`,tags:["Security","Prompt Injection","PII"],providerKey:"Promptguard",eval:{f1:94.9,precision:100,recall:90.4,testCases:5384,latency:"~150ms"}},{id:"xecguard",name:"XecGuard",description:"CyCraft XecGuard AI security gateway. Multi-policy scanning (prompt injection, harmful content, PII, system-prompt enforcement) plus RAG context grounding.",category:"partner",logo:`${ai}xecguard.svg`,tags:["Security","Policy","Grounding","RAG"],providerKey:"Xecguard"},{id:"repelloai",name:"RepelloAI Argus",description:"RepelloAI Argus scans prompts and responses against policies configured per asset in the Repello dashboard.",category:"partner",logo:`${ai}repelloai.png`,tags:["Security","Policy","Prompt Injection"],providerKey:"Repelloai"}];var an=e.i(826910);let ao=({src:e,name:t})=>{let[a,i]=(0,r.useState)(!1);return a||!e?(0,l.jsx)("div",{style:{width:28,height:28,borderRadius:6,backgroundColor:"#e5e7eb",display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,fontWeight:600,color:"#6b7280",flexShrink:0},children:t?.charAt(0)||"?"}):(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(e),alt:"",style:{width:28,height:28,borderRadius:6,objectFit:"contain",flexShrink:0},onError:()=>i(!0)})},ad=({card:e,onClick:t})=>{let[a,i]=(0,r.useState)(!1);return(0,l.jsxs)("div",{onClick:t,onMouseEnter:()=>i(!0),onMouseLeave:()=>i(!1),style:{borderRadius:12,border:a?"1px solid #93c5fd":"1px solid #e5e7eb",backgroundColor:"#ffffff",padding:"20px 20px 16px 20px",cursor:"pointer",transition:"border-color 0.15s, box-shadow 0.15s",display:"flex",flexDirection:"column",minHeight:170,boxShadow:a?"0 1px 6px rgba(59,130,246,0.08)":"none"},children:[(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:10},children:[(0,l.jsx)(ao,{src:e.logo,name:e.name}),(0,l.jsx)("span",{style:{fontSize:14,fontWeight:600,color:"#111827",lineHeight:1.3},children:e.name})]}),(0,l.jsx)("p",{className:"line-clamp-3",style:{fontSize:12,color:"#6b7280",lineHeight:1.6,margin:0,flex:1},children:e.description}),e.eval&&(0,l.jsxs)("div",{style:{marginTop:10,display:"flex",alignItems:"center",gap:4},children:[(0,l.jsx)(an.CheckCircleFilled,{style:{color:"#16a34a",fontSize:12}}),(0,l.jsxs)("span",{style:{fontSize:11,color:"#16a34a",fontWeight:500},children:["F1: ",e.eval.f1,"% · ",e.eval.testCases," test cases"]})]})]})};var ac=e.i(447566);let am={cf_denied_financial:{provider:"LitellmContentFilter",categoryName:"denied_financial_advice",guardrailNameSuggestion:"Denied Financial Advice",mode:"pre_call",defaultOn:!1},cf_denied_legal:{provider:"LitellmContentFilter",categoryName:"denied_legal_advice",guardrailNameSuggestion:"Denied Legal Advice",mode:"pre_call",defaultOn:!1},cf_denied_medical:{provider:"LitellmContentFilter",categoryName:"denied_medical_advice",guardrailNameSuggestion:"Denied Medical Advice",mode:"pre_call",defaultOn:!1},cf_denied_insults:{provider:"LitellmContentFilter",categoryName:"denied_insults",guardrailNameSuggestion:"Insults & Personal Attacks",mode:"pre_call",defaultOn:!1},cf_harmful_violence:{provider:"LitellmContentFilter",categoryName:"harmful_violence",guardrailNameSuggestion:"Harmful Violence",mode:"pre_call",defaultOn:!1},cf_harmful_self_harm:{provider:"LitellmContentFilter",categoryName:"harmful_self_harm",guardrailNameSuggestion:"Harmful Self-Harm",mode:"pre_call",defaultOn:!1},cf_harmful_child_safety:{provider:"LitellmContentFilter",categoryName:"harmful_child_safety",guardrailNameSuggestion:"Harmful Child Safety",mode:"pre_call",defaultOn:!1},cf_harmful_illegal_weapons:{provider:"LitellmContentFilter",categoryName:"harmful_illegal_weapons",guardrailNameSuggestion:"Harmful Illegal Weapons",mode:"pre_call",defaultOn:!1},cf_bias_gender:{provider:"LitellmContentFilter",categoryName:"bias_gender",guardrailNameSuggestion:"Bias: Gender",mode:"pre_call",defaultOn:!1},cf_bias_racial:{provider:"LitellmContentFilter",categoryName:"bias_racial",guardrailNameSuggestion:"Bias: Racial",mode:"pre_call",defaultOn:!1},cf_bias_religious:{provider:"LitellmContentFilter",categoryName:"bias_religious",guardrailNameSuggestion:"Bias: Religious",mode:"pre_call",defaultOn:!1},cf_bias_sexual_orientation:{provider:"LitellmContentFilter",categoryName:"bias_sexual_orientation",guardrailNameSuggestion:"Bias: Sexual Orientation",mode:"pre_call",defaultOn:!1},cf_prompt_injection_jailbreak:{provider:"LitellmContentFilter",categoryName:"prompt_injection_jailbreak",guardrailNameSuggestion:"Prompt Injection: Jailbreak",mode:"pre_call",defaultOn:!1},cf_prompt_injection_data_exfil:{provider:"LitellmContentFilter",categoryName:"prompt_injection_data_exfiltration",guardrailNameSuggestion:"Prompt Injection: Data Exfiltration",mode:"pre_call",defaultOn:!1},cf_prompt_injection_sql:{provider:"LitellmContentFilter",categoryName:"prompt_injection_sql",guardrailNameSuggestion:"Prompt Injection: SQL",mode:"pre_call",defaultOn:!1},cf_prompt_injection_malicious_code:{provider:"LitellmContentFilter",categoryName:"prompt_injection_malicious_code",guardrailNameSuggestion:"Prompt Injection: Malicious Code",mode:"pre_call",defaultOn:!1},cf_prompt_injection_system_prompt:{provider:"LitellmContentFilter",categoryName:"prompt_injection_system_prompt",guardrailNameSuggestion:"Prompt Injection: System Prompt",mode:"pre_call",defaultOn:!1},cf_toxic_abuse:{provider:"LitellmContentFilter",categoryName:"harm_toxic_abuse",guardrailNameSuggestion:"Toxic & Abusive Language",mode:"pre_call",defaultOn:!1},cf_patterns:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Pattern Matching",mode:"pre_call",defaultOn:!1},cf_keywords:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Keyword Blocking",mode:"pre_call",defaultOn:!1},block_code_execution:{provider:"BlockCodeExecution",guardrailNameSuggestion:"Block Code Execution",mode:"pre_call",defaultOn:!1},cf_competitor_intent:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Competitor Name Blocking",mode:"pre_call",defaultOn:!1},presidio:{provider:"PresidioPII",guardrailNameSuggestion:"Presidio PII",mode:"pre_call",defaultOn:!1},bedrock:{provider:"Bedrock",guardrailNameSuggestion:"Bedrock Guardrail",mode:"pre_call",defaultOn:!1},lakera:{provider:"Lakera",guardrailNameSuggestion:"Lakera",mode:"pre_call",defaultOn:!1},openai_moderation:{provider:"OpenaiModeration",guardrailNameSuggestion:"OpenAI Moderation",mode:"pre_call",defaultOn:!1},google_model_armor:{provider:"ModelArmor",guardrailNameSuggestion:"Google Cloud Model Armor",mode:"pre_call",defaultOn:!1},guardrails_ai:{provider:"GuardrailsAi",guardrailNameSuggestion:"Guardrails AI",mode:"pre_call",defaultOn:!1},zscaler:{provider:"ZscalerAiGuard",guardrailNameSuggestion:"Zscaler AI Guard",mode:"pre_call",defaultOn:!1},panw:{provider:"PanwPrismaAirs",guardrailNameSuggestion:"PANW Prisma AIRS",mode:"pre_call",defaultOn:!1},cisco_ai_defense:{provider:"CiscoAiDefense",guardrailNameSuggestion:"Cisco AI Defense",mode:"pre_call",defaultOn:!1},noma:{provider:"Noma",guardrailNameSuggestion:"Noma Security",mode:"pre_call",defaultOn:!1},aporia:{provider:"AporiaAi",guardrailNameSuggestion:"Aporia AI",mode:"pre_call",defaultOn:!1},aim:{provider:"Aim",guardrailNameSuggestion:"AIM Guardrail",mode:"pre_call",defaultOn:!1},cato_networks:{provider:"Cato Networks",guardrailNameSuggestion:"Cato Networks Guardrail",mode:"pre_call",defaultOn:!1},prompt_security:{provider:"PromptSecurity",guardrailNameSuggestion:"Prompt Security",mode:"pre_call",defaultOn:!1},lasso:{provider:"Lasso",guardrailNameSuggestion:"Lasso Guardrail",mode:"pre_call",defaultOn:!1},pangea:{provider:"Pangea",guardrailNameSuggestion:"Pangea Guardrail",mode:"pre_call",defaultOn:!1},enkryptai:{provider:"Enkryptai",guardrailNameSuggestion:"EnkryptAI",mode:"pre_call",defaultOn:!1},javelin:{provider:"Javelin",guardrailNameSuggestion:"Javelin Guardrails",mode:"pre_call",defaultOn:!1},pillar:{provider:"Pillar",guardrailNameSuggestion:"Pillar Guardrail",mode:"pre_call",defaultOn:!1},akto:{provider:"Akto",guardrailNameSuggestion:"Akto Guardrail",mode:"pre_call",defaultOn:!1},promptguard:{provider:"Promptguard",guardrailNameSuggestion:"PromptGuard",mode:"pre_call",defaultOn:!1},xecguard:{provider:"Xecguard",guardrailNameSuggestion:"XecGuard",mode:"pre_call",defaultOn:!1},repelloai:{provider:"Repelloai",guardrailNameSuggestion:"RepelloAI Argus",mode:"pre_call",defaultOn:!1}},au=({card:e,onBack:t,accessToken:a,onGuardrailCreated:s})=>{let[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("overview"),m=[{property:"Provider",value:"litellm"===e.category?"LiteLLM Content Filter":"Partner Guardrail"},...e.subcategory?[{property:"Subcategory",value:e.subcategory}]:[],..."litellm"===e.category?[{property:"Cost",value:"$0 / request"}]:[],..."litellm"===e.category?[{property:"External Dependencies",value:"None"}]:[],..."litellm"===e.category?[{property:"Latency",value:e.eval?.latency||"<1ms"}]:[]],u=e.eval?[{metric:"Precision",value:`${e.eval.precision}%`},{metric:"Recall",value:`${e.eval.recall}%`},{metric:"F1 Score",value:`${e.eval.f1}%`},{metric:"Test Cases",value:String(e.eval.testCases)},{metric:"False Positives",value:"0"},{metric:"False Negatives",value:"0"},{metric:"Latency (p50)",value:e.eval.latency}]:[],p=[{key:"overview",label:"Overview"},...e.eval?[{key:"eval",label:"Eval Results"}]:[]];return(0,l.jsxs)("div",{style:{maxWidth:960,margin:"0 auto"},children:[(0,l.jsxs)("div",{onClick:t,style:{display:"inline-flex",alignItems:"center",gap:6,color:"#5f6368",cursor:"pointer",fontSize:14,marginBottom:24},children:[(0,l.jsx)(ac.ArrowLeftOutlined,{style:{fontSize:11}}),(0,l.jsx)("span",{children:e.name})]}),(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16,marginBottom:8},children:[(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(e.logo),alt:"",style:{width:40,height:40,borderRadius:8,objectFit:"contain"},onError:e=>{e.target.style.display="none"}}),(0,l.jsx)("h1",{style:{fontSize:28,fontWeight:400,color:"#202124",margin:0,lineHeight:1.2},children:e.name})]}),(0,l.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 20px 0",lineHeight:1.6},children:e.description}),(0,l.jsx)("div",{style:{display:"flex",gap:10,marginBottom:32},children:(0,l.jsx)(i.Button,{onClick:()=>o(!0),style:{borderRadius:20,padding:"4px 20px",height:36,borderColor:"#dadce0",color:"#1a73e8",fontWeight:500,fontSize:14},children:"Create Guardrail"})}),(0,l.jsx)("div",{style:{borderBottom:"1px solid #dadce0",marginBottom:28},children:(0,l.jsx)("div",{style:{display:"flex",gap:0},children:p.map(e=>(0,l.jsx)("div",{onClick:()=>c(e.key),style:{padding:"12px 20px",fontSize:14,color:d===e.key?"#1a73e8":"#5f6368",borderBottom:d===e.key?"3px solid #1a73e8":"3px solid transparent",cursor:"pointer",fontWeight:d===e.key?500:400,marginBottom:-1},children:e.label},e.key))})}),"overview"===d&&(0,l.jsxs)("div",{style:{display:"flex",gap:64},children:[(0,l.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 12px 0"},children:"Overview"}),(0,l.jsx)("p",{style:{fontSize:14,color:"#3c4043",lineHeight:1.7,margin:"0 0 32px 0"},children:e.description}),(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 4px 0"},children:"Guardrail Details"}),(0,l.jsx)("p",{style:{fontSize:13,color:"#5f6368",margin:"0 0 16px 0"},children:"Details are as follows"}),(0,l.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:14},children:[(0,l.jsx)("thead",{children:(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #dadce0"},children:[(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500,width:200},children:"Property"}),(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500},children:e.name})]})}),(0,l.jsx)("tbody",{children:m.map((e,t)=>(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,l.jsx)("td",{style:{padding:"12px 0",color:"#3c4043"},children:e.property}),(0,l.jsx)("td",{style:{padding:"12px 0",color:"#202124"},children:e.value})]},t))})]})]}),(0,l.jsxs)("div",{style:{width:240,flexShrink:0},children:[(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Guardrail ID"}),(0,l.jsxs)("div",{style:{fontSize:13,color:"#202124",wordBreak:"break-all"},children:["litellm/",e.id]})]}),(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Type"}),(0,l.jsx)("div",{style:{fontSize:13,color:"#202124"},children:"litellm"===e.category?"Content Filter":"Partner"})]}),e.tags.length>0&&(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:8},children:"Tags"}),(0,l.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:6},children:e.tags.map(e=>(0,l.jsx)("span",{style:{fontSize:12,padding:"4px 12px",borderRadius:16,border:"1px solid #dadce0",color:"#3c4043",backgroundColor:"#fff"},children:e},e))})]})]})]}),"eval"===d&&(0,l.jsxs)("div",{children:[(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 16px 0"},children:"Eval Results"}),(0,l.jsxs)("table",{style:{width:"100%",maxWidth:560,borderCollapse:"collapse",fontSize:14},children:[(0,l.jsx)("thead",{children:(0,l.jsxs)("tr",{style:{backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 16px",color:"#5f6368",fontWeight:500},children:"Metric"}),(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 16px",color:"#5f6368",fontWeight:500},children:"Value"})]})}),(0,l.jsx)("tbody",{children:u.map((e,t)=>(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,l.jsx)("td",{style:{padding:"12px 16px",color:"#3c4043"},children:e.metric}),(0,l.jsx)("td",{style:{padding:"12px 16px",color:"#202124",fontWeight:500},children:e.value})]},t))})]})]}),(0,l.jsx)(e5,{visible:n,onClose:()=>o(!1),accessToken:a,onSuccess:()=>{o(!1),s()},preset:am[e.id]})]})},ap=({accessToken:e,onGuardrailCreated:t})=>{let[a,i]=(0,r.useState)(""),[s,n]=(0,r.useState)(null),[o,d]=(0,r.useState)(!1),c=as.filter(e=>{if(!a)return!0;let t=a.toLowerCase();return e.name.toLowerCase().includes(t)||e.description.toLowerCase().includes(t)||e.tags.some(e=>e.toLowerCase().includes(t))}),m=c.filter(e=>"litellm"===e.category),u=c.filter(e=>"partner"===e.category);return s?(0,l.jsx)(au,{card:s,onBack:()=>n(null),accessToken:e,onGuardrailCreated:t}):(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{marginBottom:24},children:(0,l.jsx)(p.Input,{size:"large",placeholder:"Search guardrails",prefix:(0,l.jsx)(t4.SearchOutlined,{style:{color:"#9ca3af"}}),value:a,onChange:e=>i(e.target.value),style:{borderRadius:8}})}),(0,l.jsxs)("div",{style:{marginBottom:40},children:[(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:4},children:[(0,l.jsx)("h2",{style:{fontSize:20,fontWeight:600,color:"#111827",margin:0},children:"LiteLLM Content Filter"}),(0,l.jsx)("span",{style:{display:"inline-flex",alignItems:"center",gap:6,fontSize:14,color:"#1a73e8",cursor:"pointer"},onClick:()=>d(!o),children:o?(0,l.jsx)(l.Fragment,{children:"Show less"}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(ar.ArrowRightOutlined,{style:{fontSize:12}}),`Show all (${m.length})`]})})]}),(0,l.jsx)("p",{style:{fontSize:13,color:"#6b7280",margin:"4px 0 20px 0"},children:"Built-in guardrails powered by LiteLLM. Zero latency, no external dependencies, no additional cost."}),(0,l.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:16},children:(o?m:m.slice(0,10)).map(e=>(0,l.jsx)(ad,{card:e,onClick:()=>n(e)},e.id))})]}),(0,l.jsxs)("div",{style:{marginBottom:40},children:[(0,l.jsx)("h2",{style:{fontSize:20,fontWeight:600,color:"#111827",margin:"0 0 4px 0"},children:"Partner Guardrails"}),(0,l.jsx)("p",{style:{fontSize:13,color:"#6b7280",margin:"4px 0 20px 0"},children:"Third-party guardrail integrations from leading AI security providers."}),(0,l.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:16},children:u.map(e=>(0,l.jsx)(ad,{card:e,onClick:()=>n(e)},e.id))})]})]})};var ag=e.i(988846),ax=e.i(837007),ah=e.i(409797),af=e.i(54131),ay=e.i(995926),aj=e.i(634831),a_=e.i(438100),ab=e.i(302202),av=e.i(328196),aw=e.i(168118),aC=e.i(663435),aN=e.i(954616),ak=e.i(912598),aS=e.i(431703),aI=e.i(135214),aA=e.i(243652);let aO=async(e,t)=>{let a=(0,m.getProxyBaseUrl)(),l=`${a}/guardrails/register`,r=await fetch(l,{method:"POST",headers:{[(0,m.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){let e=await r.json().catch(()=>({})),t=(0,aS.deriveErrorMessage)(e);throw(0,m.handleError)(t),Error(t)}return r.json()},aT=(0,aA.createQueryKeys)("guardrails");function aP(e){var t;let a=e.litellm_params??{},l=e.guardrail_info??{},r=a.headers,i=Array.isArray(r)?r.map(e=>({key:(e.key??e.name??"").toString(),value:String(e.value??"")})):"object"==typeof r&&null!==r?Object.entries(r).map(([e,t])=>({key:e,value:String(t??"")})):[],s=a.api_base??a.url??"",n=l.model??a.model??"—",o=a.forward_api_key??!0,d=Array.isArray(a.extra_headers)?a.extra_headers.filter(e=>"string"==typeof e):[];return{id:e.guardrail_id,team:e.team_id??"—",name:e.guardrail_name,endpoint:s,status:"pending_review"===(t=e.status)?"pending":"active"===t||"rejected"===t?t:"active",model:n,forwardKey:o,description:l.description??"",method:a.method??"POST",customHeaders:i,extraHeaders:d,submittedAt:function(e){if(!e)return"—";try{let t=new Date(e);return isNaN(t.getTime())?e:t.toISOString().slice(0,10)}catch{return e}}(e.submitted_at),submittedBy:e.submitted_by_email??e.submitted_by_user_id??"—",mode:a.mode,unreachable_fallback:a.unreachable_fallback,additionalProviderParams:a.additional_provider_specific_params,guardrailType:a.guardrail}}let aL={active:{label:"Active",bg:"bg-green-50",text:"text-green-700",dot:"bg-green-500"},pending:{label:"Pending Review",bg:"bg-yellow-50",text:"text-yellow-700",dot:"bg-yellow-500"},rejected:{label:"Rejected",bg:"bg-red-50",text:"text-red-700",dot:"bg-red-500"}},aB={"ML Platform":"bg-purple-100 text-purple-700","Data Science":"bg-blue-100 text-blue-700",Security:"bg-red-100 text-red-700","Customer Success":"bg-orange-100 text-orange-700",Legal:"bg-gray-100 text-gray-700",Finance:"bg-green-100 text-green-700"};function aF({label:e,value:t,color:a}){return(0,l.jsxs)("div",{className:"bg-white border border-gray-200 rounded-lg px-4 py-3",children:[(0,l.jsx)("div",{className:`text-2xl font-bold ${a}`,children:t}),(0,l.jsx)("div",{className:"text-xs text-gray-500 mt-0.5",children:e})]})}function a$({enabled:e,onToggle:t}){return(0,l.jsx)("button",{type:"button",onClick:t,role:"switch","aria-checked":e,className:`relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 ${e?"bg-blue-500":"bg-gray-200"}`,children:(0,l.jsx)("span",{className:`inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow transition-transform ${e?"translate-x-4":"translate-x-0.5"}`})})}function aE({guardrail:e,isSelected:t,isHeadersExpanded:a,onSelect:r,onToggleForwardKey:i,onToggleHeaders:s,onApprove:n,onReject:o}){let d=aL[e.status],c=aB[e.team]??"bg-gray-100 text-gray-700";return(0,l.jsxs)("div",{className:`bg-white border rounded-lg p-4 transition-all ${t?"border-blue-400 ring-1 ring-blue-200":"border-gray-200"}`,children:[(0,l.jsxs)("div",{className:"flex items-start justify-between gap-4",children:[(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-1.5 flex-wrap",children:[(0,l.jsxs)("span",{className:`text-xs font-medium px-2 py-0.5 rounded-full ${c}`,children:["Team: ",e.team]}),(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded-full ${d.bg} ${d.text}`,children:[(0,l.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${d.dot}`}),d.label]})]}),(0,l.jsx)("h3",{className:"text-sm font-semibold text-gray-900 mb-1",children:e.name}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mb-2 line-clamp-1",children:e.description}),(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)(ab.ServerIcon,{className:"h-3.5 w-3.5 text-gray-400 shrink-0"}),(0,l.jsx)("code",{className:"text-xs text-gray-500 font-mono truncate",children:e.endpoint})]}),(0,l.jsxs)("div",{className:"flex items-center gap-4 text-xs text-gray-500",children:[(0,l.jsxs)("span",{children:["Model: ",(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e.model})]}),(0,l.jsxs)("span",{children:["Submitted: ",(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e.submittedAt})]})]})]}),(0,l.jsxs)("div",{className:"flex flex-col items-end gap-2 shrink-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"text-xs text-gray-500 whitespace-nowrap",children:"Forward API Key"}),(0,l.jsx)(a$,{enabled:e.forwardKey,onToggle:i})]}),(0,l.jsxs)("div",{className:"flex items-center gap-2 mt-1",children:[(0,l.jsx)("button",{type:"button",onClick:r,className:"text-xs border border-gray-300 text-gray-600 hover:bg-gray-50 px-3 py-1.5 rounded-md transition-colors font-medium",children:t?"Close":"Review"}),"pending"===e.status&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("button",{type:"button",onClick:n,className:"text-xs bg-green-500 hover:bg-green-600 text-white px-3 py-1.5 rounded-md transition-colors font-medium",children:"Approve"}),(0,l.jsx)("button",{type:"button",onClick:o,className:"text-xs border border-red-300 text-red-600 hover:bg-red-50 px-3 py-1.5 rounded-md transition-colors font-medium",children:"Reject"})]})]})]})]}),(0,l.jsxs)("div",{className:"mt-3 pt-3 border-t border-gray-100",children:[(0,l.jsxs)("button",{type:"button",onClick:s,className:"flex items-center gap-1.5 text-xs text-gray-500 hover:text-gray-700 transition-colors",children:[a?(0,l.jsx)(af.ChevronUpIcon,{className:"h-3.5 w-3.5"}):(0,l.jsx)(ah.ChevronDownIcon,{className:"h-3.5 w-3.5"}),"Static headers",e.customHeaders.length>0&&(0,l.jsx)("span",{className:"ml-1 bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.customHeaders.length})]}),a&&(0,l.jsx)("div",{className:"mt-2",children:0===e.customHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic",children:"No static headers configured."}):(0,l.jsx)("div",{className:"space-y-1",children:e.customHeaders.map((e,t)=>(0,l.jsxs)("div",{className:"flex items-center gap-2 text-xs font-mono",children:[(0,l.jsx)("span",{className:"text-gray-500 bg-gray-50 border border-gray-200 rounded-sm px-2 py-0.5",children:e.key}),(0,l.jsx)("span",{className:"text-gray-400",children:":"}),(0,l.jsx)("span",{className:"text-gray-700 bg-gray-50 border border-gray-200 rounded-sm px-2 py-0.5",children:e.value})]},`${e.key}-${t}`))})})]})]})}function aM({label:e,children:t}){return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"text-xs font-semibold text-gray-500 mb-1",children:e}),(0,l.jsx)("div",{children:t})]})}function aR({guardrail:e,onClose:t,onApprove:a,onReject:i,onToggleForwardKey:s,onUpdateCustomHeaders:n,onUpdateExtraHeaders:o}){let[d,c]=(0,r.useState)(!1),[m,u]=(0,r.useState)(""),[p,g]=(0,r.useState)(""),[x,h]=(0,r.useState)(""),f=aL[e.status],y=aB[e.team]??"bg-gray-100 text-gray-700";return(0,l.jsx)("div",{className:"w-96 shrink-0 bg-white overflow-auto",children:(0,l.jsxs)("div",{className:"p-5",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between mb-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-1",children:[(0,l.jsxs)("span",{className:`text-xs font-medium px-2 py-0.5 rounded-full ${y}`,children:["Team: ",e.team]}),(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded-full ${f.bg} ${f.text}`,children:[(0,l.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${f.dot}`}),f.label]})]}),(0,l.jsx)("h2",{className:"text-base font-semibold text-gray-900",children:e.name}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:["Submitted by ",e.submittedBy," on ",e.submittedAt]})]}),(0,l.jsx)("button",{type:"button",onClick:t,className:"text-gray-400 hover:text-gray-600 transition-colors","aria-label":"Close detail panel",children:(0,l.jsx)(ay.XIcon,{className:"h-4 w-4"})})]}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mb-5",children:e.description}),(0,l.jsxs)("div",{className:"space-y-4",children:[(0,l.jsx)(aM,{label:"Endpoint",children:(0,l.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,l.jsx)("code",{className:"text-xs font-mono text-gray-700 break-all",children:e.endpoint}),(0,l.jsx)("a",{href:e.endpoint,target:"_blank",rel:"noopener noreferrer",className:"text-gray-400 hover:text-blue-500 shrink-0",children:(0,l.jsx)(aj.ExternalLinkIcon,{className:"h-3.5 w-3.5"})})]})}),(0,l.jsx)(aM,{label:"Method",children:(0,l.jsx)("span",{className:"text-xs font-mono font-medium text-gray-700 bg-gray-100 px-2 py-0.5 rounded-sm",children:e.method})}),(0,l.jsxs)("div",{className:"border border-blue-100 bg-blue-50 rounded-lg p-3",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,l.jsx)(a_.KeyIcon,{className:"h-3.5 w-3.5 text-blue-500"}),(0,l.jsx)("span",{className:"text-xs font-semibold text-blue-800",children:"Forward LiteLLM API Key"})]}),(0,l.jsx)(a$,{enabled:e.forwardKey,onToggle:s})]}),(0,l.jsxs)("p",{className:"text-xs text-blue-700 leading-relaxed",children:["When enabled, the caller's LiteLLM API key is forwarded as an"," ",(0,l.jsx)("code",{className:"font-mono bg-blue-100 px-1 rounded-sm",children:"Authorization"}),"header to your guardrail endpoint. This allows your guardrail to authenticate model calls using the original caller's credentials."]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-700",children:"Static headers"}),e.customHeaders.length>0&&(0,l.jsx)("span",{className:"bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.customHeaders.length})]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Sent with every request to the guardrail."}),0===e.customHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic mb-2",children:"No static headers configured."}):(0,l.jsx)("ul",{className:"list-none space-y-1 mb-2",children:e.customHeaders.map((t,a)=>(0,l.jsxs)("li",{className:"flex items-center justify-between gap-2 text-xs font-mono bg-gray-50 border border-gray-200 rounded-sm px-2 py-1.5",children:[(0,l.jsxs)("span",{className:"text-gray-700 truncate",children:[t.key,": ",t.value]}),(0,l.jsx)("button",{type:"button",onClick:()=>n(e.customHeaders.filter((e,t)=>t!==a)),className:"text-gray-400 hover:text-red-600 shrink-0","aria-label":`Remove ${t.key}`,children:(0,l.jsx)(ay.XIcon,{className:"h-3.5 w-3.5"})})]},`${t.key}-${a}`))}),(0,l.jsxs)("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-end",children:[(0,l.jsx)("input",{type:"text",value:p,onChange:e=>g(e.target.value),placeholder:"Header name (e.g. X-API-Key)",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=p.trim(),l=x.trim();a&&!e.customHeaders.some(e=>e.key.toLowerCase()===a.toLowerCase())&&(n([...e.customHeaders,{key:a,value:l}]),g(""),h(""))}}}),(0,l.jsx)("input",{type:"text",value:x,onChange:e=>h(e.target.value),placeholder:"Value",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=p.trim(),l=x.trim();a&&!e.customHeaders.some(e=>e.key.toLowerCase()===a.toLowerCase())&&(n([...e.customHeaders,{key:a,value:l}]),g(""),h(""))}}}),(0,l.jsx)("button",{type:"button",onClick:()=>{let t=p.trim(),a=x.trim();t&&!e.customHeaders.some(e=>e.key.toLowerCase()===t.toLowerCase())&&(n([...e.customHeaders,{key:t,value:a}]),g(""),h(""))},className:"text-xs font-medium text-blue-600 hover:text-blue-700 border border-blue-200 bg-blue-50 hover:bg-blue-100 px-2 py-1.5 rounded-sm transition-colors shrink-0",children:"Add"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-700",children:"Forward client headers"}),e.extraHeaders.length>0&&(0,l.jsx)("span",{className:"bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.extraHeaders.length})]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Allowed header names to forward from the client request to the guardrail (e.g. x-request-id)."}),0===e.extraHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic mb-2",children:"No forward client headers configured."}):(0,l.jsx)("ul",{className:"list-none space-y-1 mb-2",children:e.extraHeaders.map((t,a)=>(0,l.jsxs)("li",{className:"flex items-center justify-between gap-2 text-xs font-mono bg-gray-50 border border-gray-200 rounded-sm px-2 py-1.5",children:[(0,l.jsx)("span",{className:"text-gray-700 truncate",children:t}),(0,l.jsx)("button",{type:"button",onClick:()=>o(e.extraHeaders.filter((e,t)=>t!==a)),className:"text-gray-400 hover:text-red-600 shrink-0","aria-label":`Remove ${t}`,children:(0,l.jsx)(ay.XIcon,{className:"h-3.5 w-3.5"})})]},`${t}-${a}`))}),(0,l.jsxs)("div",{className:"flex gap-2",children:[(0,l.jsx)("input",{type:"text",value:m,onChange:e=>u(e.target.value),placeholder:"e.g. x-request-id",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=m.trim().toLowerCase();a&&!e.extraHeaders.map(e=>e.toLowerCase()).includes(a)&&(o([...e.extraHeaders,a]),u(""))}}}),(0,l.jsx)("button",{type:"button",onClick:()=>{let t=m.trim().toLowerCase();t&&!e.extraHeaders.map(e=>e.toLowerCase()).includes(t)&&(o([...e.extraHeaders,t]),u(""))},className:"text-xs font-medium text-blue-600 hover:text-blue-700 border border-blue-200 bg-blue-50 hover:bg-blue-100 px-2 py-1.5 rounded-sm transition-colors",children:"Add"})]})]}),(0,l.jsxs)("div",{className:"border border-gray-200 rounded-lg overflow-hidden",children:[(0,l.jsxs)("button",{type:"button",onClick:()=>c(!d),className:"w-full flex items-center justify-between px-3 py-2 text-left text-xs font-semibold text-gray-700 bg-gray-50 hover:bg-gray-100 transition-colors",children:[(0,l.jsx)("span",{children:"Equivalent config"}),d?(0,l.jsx)(af.ChevronUpIcon,{className:"h-3.5 w-3.5 text-gray-500"}):(0,l.jsx)(ah.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-500"})]}),d&&(0,l.jsx)("pre",{className:"p-3 text-xs font-mono text-gray-700 bg-white border-t border-gray-200 overflow-x-auto whitespace-pre-wrap break-all",children:function(e){let t=["litellm_settings:"," guardrails:",` - guardrail_name: "${e.name.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`," litellm_params:",` guardrail: ${e.guardrailType??"generic_guardrail_api"}`,` mode: ${e.mode??"pre_call"} # or post_call, during_call`,` api_base: ${e.endpoint||"https://your-guardrail-api.com"}`," api_key: os.environ/YOUR_GUARDRAIL_API_KEY # optional",` unreachable_fallback: ${e.unreachable_fallback??"fail_closed"} # default: fail_closed. Set to fail_open to proceed if the guardrail endpoint is unreachable.`,` forward_api_key: ${e.forwardKey}`];if(e.model&&"—"!==e.model&&t.push(` model: "${e.model}" # LLM model name sent to the guardrail for context`),e.customHeaders.length>0)for(let a of(t.push(" headers: # static headers (sent with every request)"),e.customHeaders))t.push(` ${a.key}: "${String(a.value).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`);if(e.extraHeaders.length>0)for(let a of(t.push(" extra_headers: # forward these client request headers to the guardrail"),e.extraHeaders))t.push(` - ${a}`);if(e.additionalProviderParams&&Object.keys(e.additionalProviderParams).length>0)for(let[a,l]of(t.push(" additional_provider_specific_params:"),Object.entries(e.additionalProviderParams))){let e="string"==typeof l?`"${l}"`:String(l);t.push(` ${a}: ${e}`)}return t.join("\n")}(e)})]}),(0,l.jsxs)("div",{className:"flex items-start gap-2 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,l.jsx)(aw.InfoIcon,{className:"h-3.5 w-3.5 text-gray-400 shrink-0 mt-0.5"}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 leading-relaxed",children:["This guardrail runs on a separate instance. It receives the user request and forwards the result to the next step in the pipeline. See"," ",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/adding_provider/generic_guardrail_api",target:"_blank",rel:"noopener noreferrer",className:"text-blue-500 hover:underline",children:"LiteLLM Generic Guardrail API docs"})," ","for configuration details."]})]})]}),(0,l.jsxs)("div",{className:"mt-5 pt-4 border-t border-gray-100 space-y-2",children:[(0,l.jsxs)("button",{type:"button",className:"w-full flex items-center justify-center gap-2 border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(aj.ExternalLinkIcon,{className:"h-4 w-4"}),"Test Endpoint"]}),"pending"===e.status&&(0,l.jsxs)("div",{className:"flex gap-2",children:[(0,l.jsxs)("button",{type:"button",onClick:a,className:"flex-1 flex items-center justify-center gap-1.5 bg-green-500 hover:bg-green-600 text-white text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(tO.CheckIcon,{className:"h-4 w-4"}),"Approve"]}),(0,l.jsxs)("button",{type:"button",onClick:i,className:"flex-1 flex items-center justify-center gap-1.5 border border-red-300 text-red-600 hover:bg-red-50 text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(ay.XIcon,{className:"h-4 w-4"}),"Reject"]})]})]})]})})}function aG({action:e,guardrailName:t,onConfirm:a,onCancel:r}){let i="approve"===e;return(0,l.jsx)("div",{className:"fixed inset-0 bg-black/30 flex items-center justify-center z-50",children:(0,l.jsxs)("div",{className:"bg-white rounded-xl shadow-xl p-6 max-w-sm w-full mx-4",children:[(0,l.jsx)("div",{className:`w-10 h-10 rounded-full flex items-center justify-center mb-4 ${i?"bg-green-100":"bg-red-100"}`,children:i?(0,l.jsx)(tO.CheckIcon,{className:"h-5 w-5 text-green-600"}):(0,l.jsx)(av.AlertCircleIcon,{className:"h-5 w-5 text-red-600"})}),(0,l.jsx)("h3",{className:"text-base font-semibold text-gray-900 mb-1",children:i?"Approve Guardrail":"Reject Guardrail"}),(0,l.jsxs)("p",{className:"text-sm text-gray-500 mb-5",children:["Are you sure you want to ",e," ",(0,l.jsxs)("span",{className:"font-medium text-gray-700",children:['"',t,'"']}),"?"," ",i?"This will make it active and available for use.":"This will mark it as rejected and notify the team."]}),(0,l.jsxs)("div",{className:"flex gap-3",children:[(0,l.jsx)("button",{type:"button",onClick:r,className:"flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium py-2 rounded-md transition-colors",children:"Cancel"}),(0,l.jsx)("button",{type:"button",onClick:a,className:`flex-1 text-white text-sm font-medium py-2 rounded-md transition-colors ${i?"bg-green-500 hover:bg-green-600":"bg-red-500 hover:bg-red-600"}`,children:i?"Approve":"Reject"})]})]})})}function az({accessToken:e}){let[t,a]=(0,r.useState)([]),[i,s]=(0,r.useState)({total:0,pending_review:0,active:0,rejected:0}),[n,o]=(0,r.useState)(""),[d,c]=(0,r.useState)("all"),[h,f]=(0,r.useState)(null),[j,_]=(0,r.useState)(new Set),[b,v]=(0,r.useState)(null),[w,C]=(0,r.useState)(!0),[N,k]=(0,r.useState)(null),[S,I]=(0,r.useState)(""),[A,O]=(0,r.useState)(!1),[T]=u.Form.useForm(),P=(()=>{let{accessToken:e}=(0,aI.default)(),t=(0,ak.useQueryClient)();return(0,aN.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return aO(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:aT.all})}})})();(0,r.useEffect)(()=>{let e=setTimeout(()=>I(n),300);return()=>clearTimeout(e)},[n]);let L=(0,r.useCallback)(async()=>{if(!e)return void C(!1);C(!0),k(null);try{let t="all"===d?void 0:"pending"===d?"pending_review":d,l=await (0,m.listGuardrailSubmissions)(e,{status:t,search:S.trim()||void 0});a(l.submissions.map(aP)),s(l.summary)}catch(e){k(e instanceof Error?e.message:"Failed to load submissions"),a([])}finally{C(!1)}},[e,d,S]);(0,r.useEffect)(()=>{L()},[L]);let B=t.find(e=>e.id===h)??null,F=i.total,$=i.pending_review,E=i.active,M=i.rejected;async function R(l){if(!e)return;let r=t.find(e=>e.id===l);if(!r)return;let i=!r.forwardKey;try{await (0,m.updateGuardrailCall)(e,l,{litellm_params:{forward_api_key:i}}),a(e=>e.map(e=>e.id===l?{...e,forwardKey:i}:e)),y.default.success(i?"Forward API key enabled":"Forward API key disabled")}catch{y.default.fromBackend("Failed to update forward API key")}}async function G(t,l){if(!e)return;let r={};for(let{key:e,value:t}of l)e.trim()&&(r[e.trim()]=t);try{await (0,m.updateGuardrailCall)(e,t,{litellm_params:{headers:r}}),a(e=>e.map(e=>e.id===t?{...e,customHeaders:l.filter(e=>e.key.trim())}:e)),y.default.success("Static headers updated")}catch{y.default.fromBackend("Failed to update static headers")}}async function z(t,l){if(e)try{await (0,m.updateGuardrailCall)(e,t,{litellm_params:{extra_headers:l}}),a(e=>e.map(e=>e.id===t?{...e,extraHeaders:l}:e)),y.default.success("Forward client headers updated")}catch{y.default.fromBackend("Failed to update forward client headers")}}async function D(t){if(e)try{await (0,m.approveGuardrailSubmission)(e,t),v(null),h===t&&f(null),await L(),y.default.success("Guardrail approved")}catch{y.default.fromBackend("Failed to approve guardrail")}}async function K(t){if(e)try{await (0,m.rejectGuardrailSubmission)(e,t),v(null),h===t&&f(null),await L(),y.default.success("Guardrail rejected")}catch{y.default.fromBackend("Failed to reject guardrail")}}return(0,l.jsxs)("div",{className:"flex h-full",children:[(0,l.jsxs)("div",{className:`flex-1 min-w-0 p-6 overflow-auto ${B?"border-r border-gray-200":""}`,children:[(0,l.jsxs)("div",{className:"grid grid-cols-4 gap-4 mb-6",children:[(0,l.jsx)(aF,{label:"Total Submitted",value:F,color:"text-gray-900"}),(0,l.jsx)(aF,{label:"Pending Review",value:$,color:"text-yellow-600"}),(0,l.jsx)(aF,{label:"Active",value:E,color:"text-green-600"}),(0,l.jsx)(aF,{label:"Rejected",value:M,color:"text-red-600"})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3 mb-5",children:[(0,l.jsxs)("div",{className:"relative flex-1 max-w-xs",children:[(0,l.jsx)(ag.SearchIcon,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400"}),(0,l.jsx)("input",{type:"text",placeholder:"Search guardrails...",value:n,onChange:e=>o(e.target.value),className:"w-full pl-9 pr-4 py-2 border border-gray-200 rounded-md text-sm text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500"})]}),(0,l.jsxs)("select",{value:d,onChange:e=>c(e.target.value),className:"border border-gray-200 rounded-md px-3 py-2 text-sm text-gray-700 focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500 bg-white",children:[(0,l.jsx)("option",{value:"all",children:"All Status"}),(0,l.jsx)("option",{value:"pending",children:"Pending Review"}),(0,l.jsx)("option",{value:"active",children:"Active"}),(0,l.jsx)("option",{value:"rejected",children:"Rejected"})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>O(!0),className:"ml-auto flex items-center gap-2 bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium px-4 py-2 rounded-md transition-colors",children:[(0,l.jsx)(ax.PlusIcon,{className:"h-4 w-4"}),"Add Guardrail"]})]}),(0,l.jsxs)("div",{className:"space-y-3",children:[w&&(0,l.jsx)("div",{className:"text-center py-12 text-gray-500 text-sm",children:"Loading submissions…"}),N&&(0,l.jsx)("div",{className:"text-center py-12 text-red-600 text-sm",children:N}),!w&&!N&&0===t.length&&(0,l.jsx)("div",{className:"text-center py-12 text-gray-400 text-sm",children:"No guardrails match your filters."}),!w&&!N&&t.map(e=>(0,l.jsx)(aE,{guardrail:e,isSelected:h===e.id,isHeadersExpanded:j.has(e.id),onSelect:()=>f(h===e.id?null:e.id),onToggleForwardKey:()=>R(e.id),onToggleHeaders:()=>{var t;return t=e.id,void _(e=>{let a=new Set(e);return a.has(t)?a.delete(t):a.add(t),a})},onApprove:()=>v({id:e.id,action:"approve"}),onReject:()=>v({id:e.id,action:"reject"})},e.id))]})]}),B&&(0,l.jsx)(aR,{guardrail:B,onClose:()=>f(null),onApprove:()=>v({id:B.id,action:"approve"}),onReject:()=>v({id:B.id,action:"reject"}),onToggleForwardKey:()=>R(B.id),onUpdateCustomHeaders:e=>G(B.id,e),onUpdateExtraHeaders:e=>z(B.id,e)}),b&&(0,l.jsx)(aG,{action:b.action,guardrailName:t.find(e=>e.id===b.id)?.name??"",onConfirm:()=>"approve"===b.action?D(b.id):K(b.id),onCancel:()=>v(null)}),(0,l.jsxs)(g.Modal,{title:"Submit Guardrail for Review",open:A,onCancel:()=>{O(!1),T.resetFields()},onOk:()=>T.submit(),okText:"Submit for Review",children:[(0,l.jsx)("div",{className:"rounded-md bg-blue-50 border border-blue-200 px-4 py-3 text-sm text-blue-800 mb-4",children:"Your guardrail will be sent for admin review before it becomes active."}),(0,l.jsxs)(u.Form,{form:T,layout:"vertical",initialValues:{mode:"pre_call"},onFinish:async e=>{let t={...e.extra_litellm_params?JSON.parse(e.extra_litellm_params):{},guardrail:"generic_guardrail_api",mode:e.mode,api_base:e.api_base};try{await P.mutateAsync({team_id:e.team_id,guardrail_name:e.guardrail_name,litellm_params:t,guardrail_info:e.guardrail_info?JSON.parse(e.guardrail_info):void 0}),y.default.success("Guardrail submitted for review"),O(!1),T.resetFields(),L()}catch{}},children:[(0,l.jsx)(u.Form.Item,{label:"Team",name:"team_id",rules:[{required:!0,message:"Select a team"}],children:(0,l.jsx)(aC.default,{})}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Name",name:"guardrail_name",rules:[{required:!0,message:"Enter a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"e.g. pii-detection"})}),(0,l.jsx)(u.Form.Item,{label:"Mode",name:"mode",rules:[{required:!0,message:"Select a mode"}],children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"pre_call",children:"Pre Call"}),(0,l.jsx)(x.Select.Option,{value:"post_call",children:"Post Call"}),(0,l.jsx)(x.Select.Option,{value:"during_call",children:"During Call"})]})}),(0,l.jsx)(u.Form.Item,{label:"API Base URL",name:"api_base",rules:[{required:!0,message:"Enter the API base URL"},{type:"url",message:"Must be a valid URL"}],children:(0,l.jsx)(p.Input,{placeholder:"https://your-guardrail-api.com/v1/check",className:"font-mono"})}),(0,l.jsx)(u.Form.Item,{label:"Additional litellm_params (optional)",name:"extra_litellm_params",tooltip:"JSON object merged into litellm_params. e.g. forward_api_key, headers, model, unreachable_fallback",rules:[{validator:(e,t)=>{if(!t)return Promise.resolve();try{let e=JSON.parse(t);if("object"!=typeof e||Array.isArray(e))return Promise.reject("Must be a JSON object");return Promise.resolve()}catch{return Promise.reject("Invalid JSON")}}}],children:(0,l.jsx)(p.Input.TextArea,{rows:3,className:"font-mono text-xs",placeholder:'{"forward_api_key": true, "headers": {"X-Custom": "value"}}'})}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Info (optional)",name:"guardrail_info",rules:[{validator:(e,t)=>{if(!t)return Promise.resolve();try{return JSON.parse(t),Promise.resolve()}catch{return Promise.reject("Invalid JSON")}}}],children:(0,l.jsx)(p.Input.TextArea,{rows:3,className:"font-mono text-xs",placeholder:'{"description": "Detects PII in requests"}'})})]})]})]})}let aD=({accessToken:e,userRole:t})=>{let[a,u]=(0,r.useState)([]),[p,g]=(0,r.useState)(!1),[x,h]=(0,r.useState)(!1),[f,j]=(0,r.useState)(!1),[_,b]=(0,r.useState)(!1),[v,w]=(0,r.useState)(null),[C,N]=(0,r.useState)(!1),[k,S]=(0,r.useState)(null),I=!!t&&(0,tj.isAdminRole)(t),A=async()=>{if(e){j(!0);try{let t=await (0,m.getGuardrailsList)(e);u(t.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{j(!1)}}};(0,r.useEffect)(()=>{A()},[e]);let O=()=>{A()},T=async()=>{if(v&&e){b(!0);try{await (0,m.deleteGuardrailCall)(e,v.guardrail_id),y.default.success(`Guardrail "${v.guardrail_name}" deleted successfully`),await A()}catch(e){console.error("Error deleting guardrail:",e),y.default.fromBackend("Failed to delete guardrail")}finally{b(!1),N(!1),w(null)}}},P=v&&v.litellm_params?eh(v.litellm_params.guardrail).displayName:void 0;return(0,l.jsx)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:(0,l.jsx)(n.Tabs,{defaultActiveKey:"guardrails",items:[...I?[{key:"garden",label:"Guardrail Garden",children:(0,l.jsx)(ap,{accessToken:e,onGuardrailCreated:O})},{key:"guardrails",label:"Guardrails",children:(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"flex justify-between items-center mb-4",children:(0,l.jsx)(s.Dropdown,{menu:{items:[{key:"provider",icon:(0,l.jsx)(d.PlusOutlined,{}),label:"Add Provider Guardrail",onClick:()=>{k&&S(null),g(!0)}},{key:"custom_code",icon:(0,l.jsx)(c.CodeOutlined,{}),label:"Create Custom Code Guardrail",onClick:()=>{k&&S(null),h(!0)}}]},trigger:["click"],disabled:!e,children:(0,l.jsxs)(i.Button,{disabled:!e,children:["+ Add New Guardrail ",(0,l.jsx)(o.DownOutlined,{className:"ml-2"})]})})}),k?(0,l.jsx)(t0,{guardrailId:k,onClose:()=>S(null),accessToken:e,isAdmin:I}):(0,l.jsx)(ty,{guardrailsList:a,isLoading:f,onDeleteClick:(e,t)=>{w(a.find(t=>t.guardrail_id===e)||null),N(!0)},accessToken:e,onGuardrailUpdated:A,isAdmin:I,onGuardrailClick:e=>S(e)}),(0,l.jsx)(e5,{visible:p,onClose:()=>{g(!1)},accessToken:e,onSuccess:O}),(0,l.jsx)(tZ,{visible:x,onClose:()=>{h(!1)},accessToken:e,onSuccess:O}),(0,l.jsx)(al.default,{isOpen:C,title:"Delete Guardrail",message:`Are you sure you want to delete guardrail: ${v?.guardrail_name}? This action cannot be undone.`,resourceInformationTitle:"Guardrail Information",resourceInformation:[{label:"Name",value:v?.guardrail_name},{label:"ID",value:v?.guardrail_id,code:!0},{label:"Provider",value:P},{label:"Mode",value:v?.litellm_params.mode},{label:"Default On",value:v?.litellm_params.default_on?"Yes":"No"}],onCancel:()=>{N(!1),w(null)},onOk:T,confirmLoading:_})]})},{key:"playground",label:"Test Playground",disabled:!e,children:(0,l.jsx)(aa,{guardrailsList:a,isLoading:f,accessToken:e,onClose:()=>{}})}]:[],{key:"submitted",label:"Submitted Guardrails",children:(0,l.jsx)(az,{accessToken:e})}]})})};e.s(["default",0,function(){let{accessToken:e,userRole:t}=(0,aI.default)();return(0,l.jsx)(aD,{accessToken:e,userRole:t})}],509345)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01jbmgk~h02uq.js b/litellm/proxy/_experimental/out/_next/static/chunks/01jbmgk~h02uq.js new file mode 100644 index 00000000000..6f0b448504e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01jbmgk~h02uq.js @@ -0,0 +1,420 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,602073,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64L128 192v384c0 212.1 171.9 384 384 384s384-171.9 384-384V192L512 64zm312 512c0 172.3-139.7 312-312 312S200 748.3 200 576V246l312-110 312 110v330z"}},{tag:"path",attrs:{d:"M378.4 475.1a35.91 35.91 0 00-50.9 0 35.91 35.91 0 000 50.9l129.4 129.4 2.1 2.1a33.98 33.98 0 0048.1 0L730.6 434a33.98 33.98 0 000-48.1l-2.8-2.8a33.98 33.98 0 00-48.1 0L483 579.7 378.4 475.1z"}}]},name:"safety",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["SafetyOutlined",0,n],602073)},283713,e=>{"use strict";var t=e.i(271645),i=e.i(602869),a=e.i(612256);let o="litellm_selected_worker_id";e.s(["useWorker",0,()=>{let{data:e}=(0,a.useUIConfig)(),n=e?.is_control_plane??!1,r=e?.workers??[],[s,l]=(0,t.useState)(()=>localStorage.getItem(o));(0,t.useEffect)(()=>{if(!s||0===r.length)return;let e=r.find(e=>e.worker_id===s);e&&(0,i.switchToWorkerUrl)(e.url)},[s,r]);let d=r.find(e=>e.worker_id===s)??null,p=(0,t.useCallback)(e=>{let t=r.find(t=>t.worker_id===e);t&&(l(e),localStorage.setItem(o,e),(0,i.switchToWorkerUrl)(t.url))},[r]);return{isControlPlane:n,workers:r,selectedWorkerId:s,selectedWorker:d,selectWorker:p,disconnectFromWorker:(0,t.useCallback)(()=>{l(null),localStorage.removeItem(o),(0,i.switchToWorkerUrl)(null)},[])}}])},295320,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M704 446H320c-4.4 0-8 3.6-8 8v402c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8V454c0-4.4-3.6-8-8-8zm-328 64h272v117H376V510zm272 290H376V683h272v117z"}},{tag:"path",attrs:{d:"M424 748a32 32 0 1064 0 32 32 0 10-64 0zm0-178a32 32 0 1064 0 32 32 0 10-64 0z"}},{tag:"path",attrs:{d:"M811.4 368.9C765.6 248 648.9 162 512.2 162S258.8 247.9 213 368.8C126.9 391.5 63.5 470.2 64 563.6 64.6 668 145.6 752.9 247.6 762c4.7.4 8.7-3.3 8.7-8v-60.4c0-4-3-7.4-7-7.9-27-3.4-52.5-15.2-72.1-34.5-24-23.5-37.2-55.1-37.2-88.6 0-28 9.1-54.4 26.2-76.4 16.7-21.4 40.2-36.9 66.1-43.7l37.9-10 13.9-36.7c8.6-22.8 20.6-44.2 35.7-63.5 14.9-19.2 32.6-36 52.4-50 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.3c19.9 14 37.5 30.8 52.4 50 15.1 19.3 27.1 40.7 35.7 63.5l13.8 36.6 37.8 10c54.2 14.4 92.1 63.7 92.1 120 0 33.6-13.2 65.1-37.2 88.6-19.5 19.2-44.9 31.1-71.9 34.5-4 .5-6.9 3.9-6.9 7.9V754c0 4.7 4.1 8.4 8.8 8 101.7-9.2 182.5-94 183.2-198.2.6-93.4-62.7-172.1-148.6-194.9z"}}]},name:"cloud-server",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["CloudServerOutlined",0,n],295320)},477189,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"}}]},name:"appstore",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["AppstoreOutlined",0,n],477189)},326373,e=>{"use strict";var t=e.i(21539);e.s(["Dropdown",()=>t.default])},344523,e=>{"use strict";let t=(0,e.i(475254).default)("chevrons-up-down",[["path",{d:"m7 15 5 5 5-5",key:"1hf1tw"}],["path",{d:"m7 9 5-5 5 5",key:"sgt6xg"}]]);e.s(["ChevronsUpDown",0,t],344523)},100486,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M899.6 276.5L705 396.4 518.4 147.5a8.06 8.06 0 00-12.9 0L319 396.4 124.3 276.5c-5.7-3.5-13.1 1.2-12.2 7.9L188.5 865c1.1 7.9 7.9 14 16 14h615.1c8 0 14.9-6 15.9-14l76.4-580.6c.8-6.7-6.5-11.4-12.3-7.9zm-126 534.1H250.3l-53.8-409.4 139.8 86.1L512 252.9l175.7 234.4 139.8-86.1-53.9 409.4zM512 509c-62.1 0-112.6 50.5-112.6 112.6S449.9 734.2 512 734.2s112.6-50.5 112.6-112.6S574.1 509 512 509zm0 160.9c-26.6 0-48.2-21.6-48.2-48.3 0-26.6 21.6-48.3 48.2-48.3s48.2 21.6 48.2 48.3c0 26.6-21.6 48.3-48.2 48.3z"}}]},name:"crown",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["CrownOutlined",0,n],100486)},879664,e=>{"use strict";let t=(0,e.i(475254).default)("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);e.s(["default",0,t])},596239,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M574 665.4a8.03 8.03 0 00-11.3 0L446.5 781.6c-53.8 53.8-144.6 59.5-204 0-59.5-59.5-53.8-150.2 0-204l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3l-39.8-39.8a8.03 8.03 0 00-11.3 0L191.4 526.5c-84.6 84.6-84.6 221.5 0 306s221.5 84.6 306 0l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3L574 665.4zm258.6-474c-84.6-84.6-221.5-84.6-306 0L410.3 307.6a8.03 8.03 0 000 11.3l39.7 39.7c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c53.8-53.8 144.6-59.5 204 0 59.5 59.5 53.8 150.2 0 204L665.3 562.6a8.03 8.03 0 000 11.3l39.8 39.8c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c84.5-84.6 84.5-221.5 0-306.1zM610.1 372.3a8.03 8.03 0 00-11.3 0L372.3 598.7a8.03 8.03 0 000 11.3l39.6 39.6c3.1 3.1 8.2 3.1 11.3 0l226.4-226.4c3.1-3.1 3.1-8.2 0-11.3l-39.5-39.6z"}}]},name:"link",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["LinkOutlined",0,n],596239)},652272,209261,e=>{"use strict";var t=e.i(843476),i=e.i(271645),a=e.i(447566),o=e.i(166406),n=e.i(492030),r=e.i(596239);let s=/^[a-zA-Z0-9][a-zA-Z0-9._-]*(\/[a-zA-Z0-9][a-zA-Z0-9._-]*)*$/,l=e=>e.trim().replace(/\/+$/,""),d=/\.(md|markdown|txt|json|ya?ml|toml)$/i,p=/^\d{1,3}(\.\d{1,3}){3}$/,c=/^[A-Za-z0-9-]+$/,m=/^[A-Za-z0-9._-]+$/,u=e=>e.pathname.split("/").filter(e=>""!==e),g=e=>{let t=e.split("/").filter(e=>""!==e);return t[t.length-1]??""},f=e=>e.toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,""),h=e=>{let{source:t}=e;return"github"===t.source&&t.repo?`/plugin marketplace add ${t.repo}`:("url"===t.source||"git-subdir"===t.source)&&t.url?`/plugin marketplace add ${t.url}`:`/plugin marketplace add ${e.name}`};e.s(["formatInstallCommand",0,h,"getCategoryBadgeColor",0,e=>{if(!e)return"gray";let t=e.toLowerCase();if(t.includes("development")||t.includes("dev"))return"blue";if(t.includes("productivity")||t.includes("workflow"))return"green";if(t.includes("learning")||t.includes("education"))return"purple";if(t.includes("security")||t.includes("safety"))return"red";if(t.includes("data")||t.includes("analytics"))return"orange";else if(t.includes("integration")||t.includes("api"))return"yellow";return"gray"},"isValidEmail",0,e=>!e||/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e),"isValidSemanticVersion",0,e=>!e||/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(e),"isValidSubPath",0,e=>{let t=l(e);return""!==t&&s.test(t)},"isValidUrl",0,e=>{if(!e)return!0;try{return new URL(e),!0}catch{return!1}},"parseKeywords",0,e=>e&&""!==e.trim()?e.split(",").map(e=>e.trim()).filter(e=>""!==e):[],"parseSkillSource",0,(e,t)=>{let i=(e=>{let t,i=e.trim();if(""===i||i.startsWith("//"))return null;let a=/^[a-z][a-z0-9+.-]*:\/\//i.test(i)?i:`https://${i}`;try{t=new URL(a)}catch{return null}return"https:"!==t.protocol||""!==t.username||""!==t.password||!t.hostname.includes(".")||t.hostname.startsWith("[")||p.test(t.hostname)?null:t})(e);if(!i)return null;if("github.com"===i.hostname.replace(/^www\./,""))return((e,t)=>{let i=u(e);if(i.length<2)return null;let a=i[0],o=i[1].replace(/\.git$/,"");if(!c.test(a)||!m.test(o))return null;let n=`${a}/${o}`,r=`https://github.com/${n}`,p={parsed:{source:"github",repo:n},label:`GitHub repo — ${n}`,suggestedName:f(o)};if(i.length>=4&&("tree"===i[2]||"blob"===i[2])){let e=i.slice(4),t=g(e.join("/")),a=d.test(t)?e.slice(0,-1):e;if(0===a.length)return p;let o=l(a.join("/"));return s.test(o)?{parsed:{source:"git-subdir",url:r,path:o},label:`GitHub subdir — ${n} @ ${o}`,suggestedName:f(g(o))}:null}if(2!==i.length)return null;let h=l(t??"");return""!==h?s.test(h)?{parsed:{source:"git-subdir",url:r,path:h},label:`GitHub subdir — ${n} @ ${h}`,suggestedName:f(g(h))}:null:p})(i,t);if(u(i).length<2)return null;let a=`${i.protocol}//${i.host}${i.pathname.replace(/\/+$/,"")}`,o=l(t??"");return""!==o?s.test(o)?{parsed:{source:"git-subdir",url:a,path:o},label:`Git subdir — ${a} @ ${o}`,suggestedName:f(g(o))}:null:{parsed:{source:"url",url:a},label:`Git repo — ${a}`,suggestedName:f(g(i.pathname).replace(/\.git$/,""))}},"validatePluginName",0,e=>!!e&&""!==e.trim()&&/^[a-z0-9-]+$/.test(e)],209261),e.s(["default",0,({skill:e,onBack:s})=>{let l,[d,p]=(0,i.useState)("overview"),[c,m]=(0,i.useState)(null),u=(e,t)=>{navigator.clipboard.writeText(e),m(t),setTimeout(()=>m(null),2e3)},g="github"===(l=e.source).source&&l.repo?`https://github.com/${l.repo}`:"git-subdir"===l.source&&l.url?l.path?`${l.url}/tree/main/${l.path}`:l.url:"url"===l.source&&l.url?l.url:null,f=h(e),_=[...e.category?[{property:"Category",value:e.category}]:[],...e.domain?[{property:"Domain",value:e.domain}]:[],...e.namespace?[{property:"Namespace",value:e.namespace}]:[],...e.version?[{property:"Version",value:e.version}]:[],...e.author?.name?[{property:"Author",value:e.author.name}]:[],...e.created_at?[{property:"Added",value:new Date(e.created_at).toLocaleDateString()}]:[]];return(0,t.jsxs)("div",{style:{padding:"24px 32px 24px 0"},children:[(0,t.jsxs)("div",{onClick:s,style:{display:"inline-flex",alignItems:"center",gap:6,color:"#5f6368",cursor:"pointer",fontSize:14,marginBottom:24},children:[(0,t.jsx)(a.ArrowLeftOutlined,{style:{fontSize:11}}),(0,t.jsx)("span",{children:"Skills"})]}),(0,t.jsxs)("div",{style:{marginBottom:8},children:[(0,t.jsx)("h1",{style:{fontSize:28,fontWeight:400,color:"#202124",margin:0,lineHeight:1.2},children:e.name}),e.description&&(0,t.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"8px 0 0 0",lineHeight:1.6},children:e.description})]}),(0,t.jsx)("div",{style:{borderBottom:"1px solid #dadce0",marginBottom:28,marginTop:24},children:(0,t.jsx)("div",{style:{display:"flex",gap:0},children:[{key:"overview",label:"Overview"},{key:"usage",label:"How to Use"}].map(e=>(0,t.jsx)("div",{onClick:()=>p(e.key),style:{padding:"12px 20px",fontSize:14,color:d===e.key?"#1a73e8":"#5f6368",borderBottom:d===e.key?"3px solid #1a73e8":"3px solid transparent",cursor:"pointer",fontWeight:d===e.key?500:400,marginBottom:-1},children:e.label},e.key))})}),"overview"===d&&(0,t.jsxs)("div",{style:{display:"flex",gap:64},children:[(0,t.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,t.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 4px 0"},children:"Skill Details"}),(0,t.jsx)("p",{style:{fontSize:13,color:"#5f6368",margin:"0 0 16px 0"},children:"Metadata registered with this skill"}),(0,t.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:14},children:[(0,t.jsx)("thead",{children:(0,t.jsxs)("tr",{style:{borderBottom:"1px solid #dadce0"},children:[(0,t.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500,width:160},children:"Property"}),(0,t.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500},children:e.name})]})}),(0,t.jsx)("tbody",{children:_.map((e,i)=>(0,t.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,t.jsx)("td",{style:{padding:"12px 0",color:"#3c4043"},children:e.property}),(0,t.jsx)("td",{style:{padding:"12px 0",color:"#202124"},children:e.value})]},i))})]})]}),(0,t.jsxs)("div",{style:{width:240,flexShrink:0},children:[(0,t.jsxs)("div",{style:{marginBottom:24},children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Status"}),(0,t.jsx)("span",{style:{fontSize:12,padding:"3px 10px",borderRadius:12,backgroundColor:e.enabled?"#e6f4ea":"#f1f3f4",color:e.enabled?"#137333":"#5f6368",fontWeight:500},children:e.enabled?"Public":"Draft"})]}),g&&(0,t.jsxs)("div",{style:{marginBottom:24},children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Source"}),(0,t.jsxs)("a",{href:g,target:"_blank",rel:"noopener noreferrer",style:{fontSize:13,color:"#1a73e8",wordBreak:"break-all",display:"flex",alignItems:"center",gap:4},children:[g.replace("https://",""),(0,t.jsx)(r.LinkOutlined,{style:{fontSize:11,flexShrink:0}})]})]}),e.keywords&&e.keywords.length>0&&(0,t.jsxs)("div",{style:{marginBottom:24},children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:8},children:"Tags"}),(0,t.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:6},children:e.keywords.map(e=>(0,t.jsx)("span",{style:{fontSize:12,padding:"4px 12px",borderRadius:16,border:"1px solid #dadce0",color:"#3c4043",backgroundColor:"#fff"},children:e},e))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Skill ID"}),(0,t.jsx)("div",{style:{fontSize:12,fontFamily:"monospace",color:"#3c4043",wordBreak:"break-all"},children:e.id})]})]})]}),"usage"===d&&(0,t.jsxs)("div",{style:{maxWidth:640},children:[(0,t.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 8px 0"},children:"Using this skill"}),(0,t.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 24px 0",lineHeight:1.6},children:"Once your proxy is set as a marketplace, enable this skill in Claude Code with one command:"}),(0,t.jsxs)("div",{style:{border:"1px solid #dadce0",borderRadius:8,overflow:"hidden",marginBottom:24},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"10px 16px",backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,t.jsx)("span",{style:{fontSize:13,color:"#3c4043",fontWeight:500},children:"Run in Claude Code"}),(0,t.jsxs)("button",{onClick:()=>u(f,"install"),style:{display:"flex",alignItems:"center",gap:4,fontSize:12,color:"install"===c?"#137333":"#1a73e8",background:"none",border:"none",cursor:"pointer",padding:0},children:["install"===c?(0,t.jsx)(n.CheckOutlined,{}):(0,t.jsx)(o.CopyOutlined,{}),"install"===c?"Copied":"Copy"]})]}),(0,t.jsx)("pre",{style:{margin:0,padding:"14px 16px",fontSize:14,fontFamily:"monospace",color:"#202124",backgroundColor:"#fff"},children:f})]}),(0,t.jsxs)("p",{style:{fontSize:13,color:"#5f6368",lineHeight:1.6,margin:0},children:["Don't have the marketplace configured yet?"," ",(0,t.jsx)("span",{onClick:()=>p("setup"),style:{color:"#1a73e8",cursor:"pointer"},children:"See one-time setup →"})]})]}),"setup"===d&&(0,t.jsxs)("div",{style:{maxWidth:640},children:[(0,t.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 8px 0"},children:"One-time marketplace setup"}),(0,t.jsxs)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 24px 0",lineHeight:1.6},children:["Add this to"," ",(0,t.jsx)("code",{style:{fontSize:13,backgroundColor:"#f1f3f4",padding:"1px 6px",borderRadius:4},children:"~/.claude/settings.json"})," ","to point Claude Code at your proxy:"]}),(0,t.jsxs)("div",{style:{border:"1px solid #dadce0",borderRadius:8,overflow:"hidden"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"10px 16px",backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,t.jsx)("span",{style:{fontSize:13,color:"#3c4043",fontWeight:500},children:"~/.claude/settings.json"}),(0,t.jsxs)("button",{onClick:()=>{u(JSON.stringify({extraKnownMarketplaces:{"my-org":{source:"url",url:`${window.location.origin}/claude-code/marketplace.json`}}},null,2),"settings")},style:{display:"flex",alignItems:"center",gap:4,fontSize:12,color:"settings"===c?"#137333":"#1a73e8",background:"none",border:"none",cursor:"pointer",padding:0},children:["settings"===c?(0,t.jsx)(n.CheckOutlined,{}):(0,t.jsx)(o.CopyOutlined,{}),"settings"===c?"Copied":"Copy"]})]}),(0,t.jsx)("pre",{style:{margin:0,padding:"14px 16px",fontSize:13,fontFamily:"monospace",color:"#202124",backgroundColor:"#fff"},children:JSON.stringify({extraKnownMarketplaces:{"my-org":{source:"url",url:`${window.location.origin}/claude-code/marketplace.json`}}},null,2)})]})]})]})}],652272)},798496,e=>{"use strict";var t=e.i(843476),i=e.i(152990),a=e.i(682830),o=e.i(271645),n=e.i(269200),r=e.i(427612),s=e.i(64848),l=e.i(942232),d=e.i(496020),p=e.i(977572),c=e.i(94629),m=e.i(360820),u=e.i(871943);e.s(["ModelDataTable",0,function({data:e=[],columns:g,isLoading:f=!1,defaultSorting:h=[],pagination:_,onPaginationChange:x,enablePagination:b=!1,onRowClick:y}){let[v,w]=o.default.useState(h),[j]=o.default.useState("onChange"),[S,k]=o.default.useState({}),[C,z]=o.default.useState({}),I=(0,i.useReactTable)({data:e,columns:g,state:{sorting:v,columnSizing:S,columnVisibility:C,...b&&_?{pagination:_}:{}},columnResizeMode:j,onSortingChange:w,onColumnSizingChange:k,onColumnVisibilityChange:z,...b&&x?{onPaginationChange:x}:{},getCoreRowModel:(0,a.getCoreRowModel)(),getSortedRowModel:(0,a.getSortedRowModel)(),...b?{getPaginationRowModel:(0,a.getPaginationRowModel)()}:{},enableSorting:!0,enableColumnResizing:!0,defaultColumn:{minSize:40,maxSize:500}});return(0,t.jsx)("div",{className:"rounded-lg custom-border relative",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsx)("div",{className:"relative min-w-full",children:(0,t.jsxs)(n.Table,{className:"[&_td]:py-2 [&_th]:py-2",style:{width:I.getTotalSize(),minWidth:"100%",tableLayout:"fixed"},children:[(0,t.jsx)(r.TableHead,{children:I.getHeaderGroups().map(e=>(0,t.jsx)(d.TableRow,{children:e.headers.map(e=>(0,t.jsxs)(s.TableHeaderCell,{className:`py-1 h-8 relative ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8":""} ${e.column.columnDef.meta?.className||""}`,style:{width:"actions"===e.id?120:e.getSize(),position:"actions"===e.id?"sticky":"relative",right:"actions"===e.id?0:"auto"},onClick:e.column.getCanSort()?e.column.getToggleSortingHandler():void 0,children:[(0,t.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,t.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,i.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&e.column.getCanSort()&&(0,t.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,t.jsx)(m.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,t.jsx)(u.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,t.jsx)(c.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]}),e.column.getCanResize()&&(0,t.jsx)("div",{onMouseDown:e.getResizeHandler(),onTouchStart:e.getResizeHandler(),className:`absolute right-0 top-0 h-full w-2 cursor-col-resize select-none touch-none ${e.column.getIsResizing()?"bg-blue-500":"hover:bg-blue-200"}`})]},e.id))},e.id))}),(0,t.jsx)(l.TableBody,{children:f?(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(p.TableCell,{colSpan:g.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"🚅 Loading models..."})})})}):I.getRowModel().rows.length>0?I.getRowModel().rows.map(e=>(0,t.jsx)(d.TableRow,{onClick:()=>y?.(e.original),className:y?"cursor-pointer hover:bg-gray-50":"",children:e.getVisibleCells().map(e=>(0,t.jsx)(p.TableCell,{className:`py-0.5 overflow-hidden ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8":""} ${e.column.columnDef.meta?.className||""}`,style:{width:"actions"===e.column.id?120:e.column.getSize(),position:"actions"===e.column.id?"sticky":"relative",right:"actions"===e.column.id?0:"auto"},children:(0,i.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(p.TableCell,{colSpan:g.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"No models found"})})})})})]})})})})}])},339019,865361,e=>{"use strict";var t,i,a=((t={}).AUDIO_SPEECH="audio_speech",t.AUDIO_TRANSCRIPTION="audio_transcription",t.IMAGE_GENERATION="image_generation",t.VIDEO_GENERATION="video_generation",t.CHAT="chat",t.RESPONSES="responses",t.IMAGE_EDITS="image_edits",t.ANTHROPIC_MESSAGES="anthropic_messages",t.EMBEDDING="embedding",t),o=((i={}).IMAGE="image",i.VIDEO="video",i.CHAT="chat",i.RESPONSES="responses",i.IMAGE_EDITS="image_edits",i.ANTHROPIC_MESSAGES="anthropic_messages",i.EMBEDDINGS="embeddings",i.SPEECH="speech",i.TRANSCRIPTION="transcription",i.A2A_AGENTS="a2a_agents",i.MCP="mcp",i.REALTIME="realtime",i.INTERACTIONS="interactions",i);let n={image_generation:"image",video_generation:"video",chat:"chat",responses:"responses",image_edits:"image_edits",anthropic_messages:"anthropic_messages",audio_speech:"speech",audio_transcription:"transcription",embedding:"embeddings"};e.s(["EndpointType",()=>o,"getEndpointType",0,e=>Object.values(a).includes(e)?n[e]:"chat"],865361),e.s(["generateCodeSnippet",0,e=>{let t,{apiKeySource:i,accessToken:a,apiKey:n,inputMessage:r,chatHistory:s,selectedTags:l,selectedVectorStores:d,selectedGuardrails:p,selectedPolicies:c,selectedMCPServers:m,mcpServers:u,mcpServerToolRestrictions:g,selectedVoice:f,endpointType:h,selectedModel:_,selectedSdk:x,proxySettings:b}=e,y="session"===i?a:n,v=window.location.origin,w=b?.LITELLM_UI_API_DOC_BASE_URL;w&&w.trim()?v=w:b?.PROXY_BASE_URL&&(v=b.PROXY_BASE_URL);let j=r||"Your prompt here",S=j.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n"),k=s.filter(e=>!e.isImage).map(({role:e,content:t})=>({role:e,content:t})),C={};l.length>0&&(C.tags=l),d.length>0&&(C.vector_stores=d),p.length>0&&(C.guardrails=p),c.length>0&&(C.policies=c);let z=_||"your-model-name",I="azure"===x?`import openai + +client = openai.AzureOpenAI( + api_key="${y||"YOUR_LITELLM_API_KEY"}", + azure_endpoint="${v}", + api_version="2024-02-01" +)`:`import openai + +client = openai.OpenAI( + api_key="${y||"YOUR_LITELLM_API_KEY"}", + base_url="${v}" +)`;switch(h){case o.CHAT:{let e=Object.keys(C).length>0,i="";if(e){let e=JSON.stringify({metadata:C},null,2).split("\n").map(e=>" ".repeat(4)+e).join("\n").trim();i=`, + extra_body=${e}`}let a=k.length>0?k:[{role:"user",content:j}];t=` +import base64 + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Example with text only +response = client.chat.completions.create( + model="${z}", + messages=${JSON.stringify(a,null,4)}${i} +) + +print(response) + +# Example with image or PDF (uncomment and provide file path to use) +# base64_file = encode_image("path/to/your/file.jpg") # or .pdf +# response_with_file = client.chat.completions.create( +# model="${z}", +# messages=[ +# { +# "role": "user", +# "content": [ +# { +# "type": "text", +# "text": "${S}" +# }, +# { +# "type": "image_url", +# "image_url": { +# "url": f"data:image/jpeg;base64,{base64_file}" # or data:application/pdf;base64,{base64_file} +# } +# } +# ] +# } +# ]${i} +# ) +# print(response_with_file) +`;break}case o.RESPONSES:{let e=Object.keys(C).length>0,i="";if(e){let e=JSON.stringify({metadata:C},null,2).split("\n").map(e=>" ".repeat(4)+e).join("\n").trim();i=`, + extra_body=${e}`}let a=k.length>0?k:[{role:"user",content:j}];t=` +import base64 + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Example with text only +response = client.responses.create( + model="${z}", + input=${JSON.stringify(a,null,4)}${i} +) + +print(response.output_text) + +# Example with image or PDF (uncomment and provide file path to use) +# base64_file = encode_image("path/to/your/file.jpg") # or .pdf +# response_with_file = client.responses.create( +# model="${z}", +# input=[ +# { +# "role": "user", +# "content": [ +# {"type": "input_text", "text": "${S}"}, +# { +# "type": "input_image", +# "image_url": f"data:image/jpeg;base64,{base64_file}", # or data:application/pdf;base64,{base64_file} +# }, +# ], +# } +# ]${i} +# ) +# print(response_with_file.output_text) +`;break}case o.IMAGE:t="azure"===x?` +# NOTE: The Azure SDK does not have a direct equivalent to the multi-modal 'responses.create' method shown for OpenAI. +# This snippet uses 'client.images.generate' and will create a new image based on your prompt. +# It does not use the uploaded image, as 'client.images.generate' does not support image inputs in this context. +import os +import requests +import json +import time +from PIL import Image + +result = client.images.generate( + model="${z}", + prompt="${r}", + n=1 +) + +json_response = json.loads(result.model_dump_json()) + +# Set the directory for the stored image +image_dir = os.path.join(os.curdir, 'images') + +# If the directory doesn't exist, create it +if not os.path.isdir(image_dir): + os.mkdir(image_dir) + +# Initialize the image path +image_filename = f"generated_image_{int(time.time())}.png" +image_path = os.path.join(image_dir, image_filename) + +try: + # Retrieve the generated image + if json_response.get("data") && len(json_response["data"]) > 0 && json_response["data"][0].get("url"): + image_url = json_response["data"][0]["url"] + generated_image = requests.get(image_url).content + with open(image_path, "wb") as image_file: + image_file.write(generated_image) + + print(f"Image saved to {image_path}") + # Display the image + image = Image.open(image_path) + image.show() + else: + print("Could not find image URL in response.") + print("Full response:", json_response) +except Exception as e: + print(f"An error occurred: {e}") + print("Full response:", json_response) +`:` +import base64 +import os +import time +import json +from PIL import Image +import requests + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Helper function to create a file (simplified for this example) +def create_file(image_path): + # In a real implementation, this would upload the file to OpenAI + # For this example, we'll just return a placeholder ID + return f"file_{os.path.basename(image_path).replace('.', '_')}" + +# The prompt entered by the user +prompt = "${S}" + +# Encode images to base64 +base64_image1 = encode_image("body-lotion.png") +base64_image2 = encode_image("soap.png") + +# Create file IDs +file_id1 = create_file("body-lotion.png") +file_id2 = create_file("incense-kit.png") + +response = client.responses.create( + model="${z}", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image1}", + }, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image2}", + }, + { + "type": "input_image", + "file_id": file_id1, + }, + { + "type": "input_image", + "file_id": file_id2, + } + ], + } + ], + tools=[{"type": "image_generation"}], +) + +# Process the response +image_generation_calls = [ + output + for output in response.output + if output.type == "image_generation_call" +] + +image_data = [output.result for output in image_generation_calls] + +if image_data: + image_base64 = image_data[0] + image_filename = f"edited_image_{int(time.time())}.png" + with open(image_filename, "wb") as f: + f.write(base64.b64decode(image_base64)) + print(f"Image saved to {image_filename}") +else: + # If no image is generated, there might be a text response with an explanation + text_response = [output.text for output in response.output if hasattr(output, 'text')] + if text_response: + print("No image generated. Model response:") + print("\\n".join(text_response)) + else: + print("No image data found in response.") + print("Full response for debugging:") + print(response) +`;break;case o.IMAGE_EDITS:t="azure"===x?` +import base64 +import os +import time +import json +from PIL import Image +import requests + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# The prompt entered by the user +prompt = "${S}" + +# Encode images to base64 +base64_image1 = encode_image("body-lotion.png") +base64_image2 = encode_image("soap.png") + +# Create file IDs +file_id1 = create_file("body-lotion.png") +file_id2 = create_file("incense-kit.png") + +response = client.responses.create( + model="${z}", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image1}", + }, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image2}", + }, + { + "type": "input_image", + "file_id": file_id1, + }, + { + "type": "input_image", + "file_id": file_id2, + } + ], + } + ], + tools=[{"type": "image_generation"}], +) + +# Process the response +image_generation_calls = [ + output + for output in response.output + if output.type == "image_generation_call" +] + +image_data = [output.result for output in image_generation_calls] + +if image_data: + image_base64 = image_data[0] + image_filename = f"edited_image_{int(time.time())}.png" + with open(image_filename, "wb") as f: + f.write(base64.b64decode(image_base64)) + print(f"Image saved to {image_filename}") +else: + # If no image is generated, there might be a text response with an explanation + text_response = [output.text for output in response.output if hasattr(output, 'text')] + if text_response: + print("No image generated. Model response:") + print("\\n".join(text_response)) + else: + print("No image data found in response.") + print("Full response for debugging:") + print(response) +`:` +import base64 +import os +import time + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Helper function to create a file (simplified for this example) +def create_file(image_path): + # In a real implementation, this would upload the file to OpenAI + # For this example, we'll just return a placeholder ID + return f"file_{os.path.basename(image_path).replace('.', '_')}" + +# The prompt entered by the user +prompt = "${S}" + +# Encode images to base64 +base64_image1 = encode_image("body-lotion.png") +base64_image2 = encode_image("soap.png") + +# Create file IDs +file_id1 = create_file("body-lotion.png") +file_id2 = create_file("incense-kit.png") + +response = client.responses.create( + model="${z}", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image1}", + }, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image2}", + }, + { + "type": "input_image", + "file_id": file_id1, + }, + { + "type": "input_image", + "file_id": file_id2, + } + ], + } + ], + tools=[{"type": "image_generation"}], +) + +# Process the response +image_generation_calls = [ + output + for output in response.output + if output.type == "image_generation_call" +] + +image_data = [output.result for output in image_generation_calls] + +if image_data: + image_base64 = image_data[0] + image_filename = f"edited_image_{int(time.time())}.png" + with open(image_filename, "wb") as f: + f.write(base64.b64decode(image_base64)) + print(f"Image saved to {image_filename}") +else: + # If no image is generated, there might be a text response with an explanation + text_response = [output.text for output in response.output if hasattr(output, 'text')] + if text_response: + print("No image generated. Model response:") + print("\\n".join(text_response)) + else: + print("No image data found in response.") + print("Full response for debugging:") + print(response) +`;break;case o.EMBEDDINGS:t=` +response = client.embeddings.create( + input="${r||"Your string here"}", + model="${z}", + encoding_format="base64" # or "float" +) + +print(response.data[0].embedding) +`;break;case o.TRANSCRIPTION:t=` +# Open the audio file +audio_file = open("path/to/your/audio/file.mp3", "rb") + +# Make the transcription request +response = client.audio.transcriptions.create( + model="${z}", + file=audio_file${r?`, + prompt="${r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:""} +) + +print(response.text) +`;break;case o.SPEECH:t=` +# Make the text-to-speech request +response = client.audio.speech.create( + model="${z}", + input="${r||"Your text to convert to speech here"}", + voice="${f}" # Options: alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer +) + +# Save the audio to a file +output_filename = "output_speech.mp3" +response.stream_to_file(output_filename) +print(f"Audio saved to {output_filename}") + +# Optional: Customize response format and speed +# response = client.audio.speech.create( +# model="${z}", +# input="${r||"Your text to convert to speech here"}", +# voice="alloy", +# response_format="mp3", # Options: mp3, opus, aac, flac, wav, pcm +# speed=1.0 # Range: 0.25 to 4.0 +# ) +# response.stream_to_file("output_speech.mp3") +`;break;default:t="\n# Code generation for this endpoint is not implemented yet."}return`${I} +${t}`}],339019)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01m7lab3u92-v.js b/litellm/proxy/_experimental/out/_next/static/chunks/01m7lab3u92-v.js new file mode 100644 index 00000000000..dd0196da59e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01m7lab3u92-v.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,244451,e=>{"use strict";let t;e.i(247167);var i=e.i(271645),n=e.i(343794),o=e.i(242064),a=e.i(763731),l=e.i(174428);let r=80*Math.PI,c=e=>{let{dotClassName:t,style:o,hasCircleCls:a}=e;return i.createElement("circle",{className:(0,n.default)(`${t}-circle`,{[`${t}-circle-bg`]:a}),r:40,cx:50,cy:50,strokeWidth:20,style:o})},s=({percent:e,prefixCls:t})=>{let o=`${t}-dot`,a=`${o}-holder`,s=`${a}-hidden`,[d,u]=i.useState(!1);(0,l.default)(()=>{0!==e&&u(!0)},[0!==e]);let m=Math.max(Math.min(e,100),0);if(!d)return null;let p={strokeDashoffset:`${r/4}`,strokeDasharray:`${r*m/100} ${r*(100-m)/100}`};return i.createElement("span",{className:(0,n.default)(a,`${o}-progress`,m<=0&&s)},i.createElement("svg",{viewBox:"0 0 100 100",role:"progressbar","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":m},i.createElement(c,{dotClassName:o,hasCircleCls:!0}),i.createElement(c,{dotClassName:o,style:p})))};function d(e){let{prefixCls:t,percent:o=0}=e,a=`${t}-dot`,l=`${a}-holder`,r=`${l}-hidden`;return i.createElement(i.Fragment,null,i.createElement("span",{className:(0,n.default)(l,o>0&&r)},i.createElement("span",{className:(0,n.default)(a,`${t}-dot-spin`)},[1,2,3,4].map(e=>i.createElement("i",{className:`${t}-dot-item`,key:e})))),i.createElement(s,{prefixCls:t,percent:o}))}function u(e){var t;let{prefixCls:o,indicator:l,percent:r}=e,c=`${o}-dot`;return l&&i.isValidElement(l)?(0,a.cloneElement)(l,{className:(0,n.default)(null==(t=l.props)?void 0:t.className,c),percent:r}):i.createElement(d,{prefixCls:o,percent:r})}e.i(296059);var m=e.i(694758),p=e.i(183293),g=e.i(246422),f=e.i(838378);let b=new m.Keyframes("antSpinMove",{to:{opacity:1}}),h=new m.Keyframes("antRotate",{to:{transform:"rotate(405deg)"}}),v=(0,g.genStyleHooks)("Spin",e=>(e=>{let{componentCls:t,calc:i}=e;return{[t]:Object.assign(Object.assign({},(0,p.resetComponent)(e)),{position:"absolute",display:"none",color:e.colorPrimary,fontSize:0,textAlign:"center",verticalAlign:"middle",opacity:0,transition:`transform ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`,"&-spinning":{position:"relative",display:"inline-block",opacity:1},[`${t}-text`]:{fontSize:e.fontSize,paddingTop:i(i(e.dotSize).sub(e.fontSize)).div(2).add(2).equal()},"&-fullscreen":{position:"fixed",width:"100vw",height:"100vh",backgroundColor:e.colorBgMask,zIndex:e.zIndexPopupBase,inset:0,display:"flex",alignItems:"center",flexDirection:"column",justifyContent:"center",opacity:0,visibility:"hidden",transition:`all ${e.motionDurationMid}`,"&-show":{opacity:1,visibility:"visible"},[t]:{[`${t}-dot-holder`]:{color:e.colorWhite},[`${t}-text`]:{color:e.colorTextLightSolid}}},"&-nested-loading":{position:"relative",[`> div > ${t}`]:{position:"absolute",top:0,insetInlineStart:0,zIndex:4,display:"block",width:"100%",height:"100%",maxHeight:e.contentHeight,[`${t}-dot`]:{position:"absolute",top:"50%",insetInlineStart:"50%",margin:i(e.dotSize).mul(-1).div(2).equal()},[`${t}-text`]:{position:"absolute",top:"50%",width:"100%",textShadow:`0 1px 2px ${e.colorBgContainer}`},[`&${t}-show-text ${t}-dot`]:{marginTop:i(e.dotSize).div(2).mul(-1).sub(10).equal()},"&-sm":{[`${t}-dot`]:{margin:i(e.dotSizeSM).mul(-1).div(2).equal()},[`${t}-text`]:{paddingTop:i(i(e.dotSizeSM).sub(e.fontSize)).div(2).add(2).equal()},[`&${t}-show-text ${t}-dot`]:{marginTop:i(e.dotSizeSM).div(2).mul(-1).sub(10).equal()}},"&-lg":{[`${t}-dot`]:{margin:i(e.dotSizeLG).mul(-1).div(2).equal()},[`${t}-text`]:{paddingTop:i(i(e.dotSizeLG).sub(e.fontSize)).div(2).add(2).equal()},[`&${t}-show-text ${t}-dot`]:{marginTop:i(e.dotSizeLG).div(2).mul(-1).sub(10).equal()}}},[`${t}-container`]:{position:"relative",transition:`opacity ${e.motionDurationSlow}`,"&::after":{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,zIndex:10,width:"100%",height:"100%",background:e.colorBgContainer,opacity:0,transition:`all ${e.motionDurationSlow}`,content:'""',pointerEvents:"none"}},[`${t}-blur`]:{clear:"both",opacity:.5,userSelect:"none",pointerEvents:"none","&::after":{opacity:.4,pointerEvents:"auto"}}},"&-tip":{color:e.spinDotDefault},[`${t}-dot-holder`]:{width:"1em",height:"1em",fontSize:e.dotSize,display:"inline-block",transition:`transform ${e.motionDurationSlow} ease, opacity ${e.motionDurationSlow} ease`,transformOrigin:"50% 50%",lineHeight:1,color:e.colorPrimary,"&-hidden":{transform:"scale(0.3)",opacity:0}},[`${t}-dot-progress`]:{position:"absolute",inset:0},[`${t}-dot`]:{position:"relative",display:"inline-block",fontSize:e.dotSize,width:"1em",height:"1em","&-item":{position:"absolute",display:"block",width:i(e.dotSize).sub(i(e.marginXXS).div(2)).div(2).equal(),height:i(e.dotSize).sub(i(e.marginXXS).div(2)).div(2).equal(),background:"currentColor",borderRadius:"100%",transform:"scale(0.75)",transformOrigin:"50% 50%",opacity:.3,animationName:b,animationDuration:"1s",animationIterationCount:"infinite",animationTimingFunction:"linear",animationDirection:"alternate","&:nth-child(1)":{top:0,insetInlineStart:0,animationDelay:"0s"},"&:nth-child(2)":{top:0,insetInlineEnd:0,animationDelay:"0.4s"},"&:nth-child(3)":{insetInlineEnd:0,bottom:0,animationDelay:"0.8s"},"&:nth-child(4)":{bottom:0,insetInlineStart:0,animationDelay:"1.2s"}},"&-spin":{transform:"rotate(45deg)",animationName:h,animationDuration:"1.2s",animationIterationCount:"infinite",animationTimingFunction:"linear"},"&-circle":{strokeLinecap:"round",transition:["stroke-dashoffset","stroke-dasharray","stroke","stroke-width","opacity"].map(t=>`${t} ${e.motionDurationSlow} ease`).join(","),fillOpacity:0,stroke:"currentcolor"},"&-circle-bg":{stroke:e.colorFillSecondary}},[`&-sm ${t}-dot`]:{"&, &-holder":{fontSize:e.dotSizeSM}},[`&-sm ${t}-dot-holder`]:{i:{width:i(i(e.dotSizeSM).sub(i(e.marginXXS).div(2))).div(2).equal(),height:i(i(e.dotSizeSM).sub(i(e.marginXXS).div(2))).div(2).equal()}},[`&-lg ${t}-dot`]:{"&, &-holder":{fontSize:e.dotSizeLG}},[`&-lg ${t}-dot-holder`]:{i:{width:i(i(e.dotSizeLG).sub(e.marginXXS)).div(2).equal(),height:i(i(e.dotSizeLG).sub(e.marginXXS)).div(2).equal()}},[`&${t}-show-text ${t}-text`]:{display:"block"}})}})((0,f.mergeToken)(e,{spinDotDefault:e.colorTextDescription})),e=>{let{controlHeightLG:t,controlHeight:i}=e;return{contentHeight:400,dotSize:t/2,dotSizeSM:.35*t,dotSizeLG:i}}),S=[[30,.05],[70,.03],[96,.01]];var $=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};let y=e=>{var a;let{prefixCls:l,spinning:r=!0,delay:c=0,className:s,rootClassName:d,size:m="default",tip:p,wrapperClassName:g,style:f,children:b,fullscreen:h=!1,indicator:y,percent:C}=e,k=$(e,["prefixCls","spinning","delay","className","rootClassName","size","tip","wrapperClassName","style","children","fullscreen","indicator","percent"]),{getPrefixCls:x,direction:z,className:E,style:N,indicator:w}=(0,o.useComponentConfig)("spin"),j=x("spin",l),[I,O,M]=v(j),[B,D]=i.useState(()=>r&&(!r||!c||!!Number.isNaN(Number(c)))),T=function(e,t){let[n,o]=i.useState(0),a=i.useRef(null),l="auto"===t;return i.useEffect(()=>(l&&e&&(o(0),a.current=setInterval(()=>{o(e=>{let t=100-e;for(let i=0;i{a.current&&(clearInterval(a.current),a.current=null)}),[l,e]),l?n:t}(B,C);i.useEffect(()=>{if(r){let e=function(e,t,i){var n,o=i||{},a=o.noTrailing,l=void 0!==a&&a,r=o.noLeading,c=void 0!==r&&r,s=o.debounceMode,d=void 0===s?void 0:s,u=!1,m=0;function p(){n&&clearTimeout(n)}function g(){for(var i=arguments.length,o=Array(i),a=0;ae?c?(m=Date.now(),l||(n=setTimeout(d?f:g,e))):g():!0!==l&&(n=setTimeout(d?f:g,void 0===d?e-s:e)))}return g.cancel=function(e){var t=(e||{}).upcomingOnly;p(),u=!(void 0!==t&&t)},g}(c,()=>{D(!0)},{debounceMode:false});return e(),()=>{var t;null==(t=null==e?void 0:e.cancel)||t.call(e)}}D(!1)},[c,r]);let P=i.useMemo(()=>void 0!==b&&!h,[b,h]),H=(0,n.default)(j,E,{[`${j}-sm`]:"small"===m,[`${j}-lg`]:"large"===m,[`${j}-spinning`]:B,[`${j}-show-text`]:!!p,[`${j}-rtl`]:"rtl"===z},s,!h&&d,O,M),A=(0,n.default)(`${j}-container`,{[`${j}-blur`]:B}),q=null!=(a=null!=y?y:w)?a:t,R=Object.assign(Object.assign({},N),f),_=i.createElement("div",Object.assign({},k,{style:R,className:H,"aria-live":"polite","aria-busy":B}),i.createElement(u,{prefixCls:j,indicator:q,percent:T}),p&&(P||h)?i.createElement("div",{className:`${j}-text`},p):null);return I(P?i.createElement("div",Object.assign({},k,{className:(0,n.default)(`${j}-nested-loading`,g,O,M)}),B&&i.createElement("div",{key:"loading"},_),i.createElement("div",{className:A,key:"container"},b)):h?i.createElement("div",{className:(0,n.default)(`${j}-fullscreen`,{[`${j}-fullscreen-show`]:B},d,O,M)},_):_)};y.setDefaultIndicator=e=>{t=e},e.s(["default",0,y],244451)},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,a){return t.createElement(o.default,(0,i.default)({},e,{ref:a,icon:n}))});let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var r=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:l}))}),c=e.i(801312),s=e.i(286612),d=e.i(343794),u=e.i(211577),m=e.i(410160),p=e.i(209428),g=e.i(392221),f=e.i(914949),b=e.i(404948),h=e.i(244009);e.i(883110);let v={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var S=[10,20,50,100];let $=function(e){var i=e.pageSizeOptions,n=void 0===i?S:i,o=e.locale,a=e.changeSize,l=e.pageSize,r=e.goButton,c=e.quickGo,s=e.rootPrefixCls,d=e.disabled,u=e.buildOptionText,m=e.showSizeChanger,p=e.sizeChangerRender,f=t.default.useState(""),h=(0,g.default)(f,2),v=h[0],$=h[1],y=function(){return!v||Number.isNaN(v)?void 0:Number(v)},C="function"==typeof u?u:function(e){return"".concat(e," ").concat(o.items_per_page)},k=function(e){""!==v&&(e.keyCode===b.default.ENTER||"click"===e.type)&&($(""),null==c||c(y()))},x="".concat(s,"-options");if(!m&&!c)return null;var z=null,E=null,N=null;return m&&p&&(z=p({disabled:d,size:l,onSizeChange:function(e){null==a||a(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===l.toString()})?n:n.concat([l]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:C(e),value:e}})})),c&&(r&&(N="boolean"==typeof r?t.default.createElement("button",{type:"button",onClick:k,onKeyUp:k,disabled:d,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:k,onKeyUp:k},r)),E=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:d,type:"text",value:v,onChange:function(e){$(e.target.value)},onKeyUp:k,onBlur:function(e){r||""===v||($(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(s,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(s,"-item"))>=0)||null==c||c(y()))},"aria-label":o.page}),o.page,N)),t.default.createElement("li",{className:x},z,E)},y=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,a=e.className,l=e.showTitle,r=e.onClick,c=e.onKeyPress,s=e.itemRender,m="".concat(i,"-item"),p=(0,d.default)(m,"".concat(m,"-").concat(n),(0,u.default)((0,u.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),a),g=s(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return g?t.default.createElement("li",{title:l?String(n):null,className:p,onClick:function(){r(n)},onKeyDown:function(e){c(e,r,n)},tabIndex:0},g):null};var C=function(e,t,i){return i};function k(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function z(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let E=function(e){var n,o,a,l,r=e.prefixCls,c=void 0===r?"rc-pagination":r,s=e.selectPrefixCls,S=e.className,E=e.current,N=e.defaultCurrent,w=e.total,j=void 0===w?0:w,I=e.pageSize,O=e.defaultPageSize,M=e.onChange,B=void 0===M?k:M,D=e.hideOnSinglePage,T=e.align,P=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,q=e.showTitle,R=void 0===q||q,_=e.onShowSizeChange,L=void 0===_?k:_,X=e.locale,W=void 0===X?v:X,K=e.style,F=e.totalBoundaryShowSizeChanger,G=e.disabled,U=e.simple,J=e.showTotal,V=e.showSizeChanger,Q=void 0===V?j>(void 0===F?50:F):V,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?C:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,ea=e.nextIcon,el=t.default.useRef(null),er=(0,f.default)(10,{value:I,defaultValue:void 0===O?10:O}),ec=(0,g.default)(er,2),es=ec[0],ed=ec[1],eu=(0,f.default)(1,{value:E,defaultValue:void 0===N?1:N,postState:function(e){return Math.max(1,Math.min(e,z(void 0,es,j)))}}),em=(0,g.default)(eu,2),ep=em[0],eg=em[1],ef=t.default.useState(ep),eb=(0,g.default)(ef,2),eh=eb[0],ev=eb[1];(0,t.useEffect)(function(){ev(ep)},[ep]);var eS=Math.max(1,ep-(A?3:5)),e$=Math.min(z(void 0,es,j),ep+(A?3:5));function ey(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,p.default)({},e))),o}function eC(e){var t=e.target.value,i=z(void 0,es,j);return""===t?t:Number.isNaN(Number(t))?eh:t>=i?i:Number(t)}var ek=j>es&&H;function ex(e){var t=eC(e);switch(t!==eh&&ev(t),e.keyCode){case b.default.ENTER:ez(t);break;case b.default.UP:ez(t-1);break;case b.default.DOWN:ez(t+1)}}function ez(e){if(x(e)&&e!==ep&&x(j)&&j>0&&!G){var t=z(void 0,es,j),i=e;return e>t?i=t:e<1&&(i=1),i!==eh&&ev(i),eg(i),null==B||B(i,es),i}return ep}var eE=ep>1,eN=ep2?i-2:0),o=2;oj?j:ep*es])),eH=null,eA=z(void 0,es,j);if(D&&j<=es)return null;var eq=[],eR={rootPrefixCls:c,onClick:ez,onKeyPress:eM,showTitle:R,itemRender:et,page:-1},e_=ep-1>0?ep-1:0,eL=ep+1=2*eG&&3!==ep&&(eq[0]=t.default.cloneElement(eq[0],{className:(0,d.default)("".concat(c,"-item-after-jump-prev"),eq[0].props.className)}),eq.unshift(eD)),eA-ep>=2*eG&&ep!==eA-2){var e2=eq[eq.length-1];eq[eq.length-1]=t.default.cloneElement(e2,{className:(0,d.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),eq.push(eH)}1!==eZ&&eq.unshift(t.default.createElement(y,(0,i.default)({},eR,{key:1,page:1}))),e0!==eA&&eq.push(t.default.createElement(y,(0,i.default)({},eR,{key:eA,page:eA})))}var e3=(n=et(e_,"prev",ey(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!eE}):n);if(e3){var e4=!eE||!eA;e3=t.default.createElement("li",{title:R?W.prev_page:null,onClick:ew,tabIndex:e4?null:0,onKeyDown:function(e){eM(e,ew)},className:(0,d.default)("".concat(c,"-prev"),(0,u.default)({},"".concat(c,"-disabled"),e4)),"aria-disabled":e4},e3)}var e9=(o=et(eL,"next",ey(ea,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eN}):o);e9&&(U?(a=!eN,l=eE?0:null):l=(a=!eN||!eA)?null:0,e9=t.default.createElement("li",{title:R?W.next_page:null,onClick:ej,tabIndex:l,onKeyDown:function(e){eM(e,ej)},className:(0,d.default)("".concat(c,"-next"),(0,u.default)({},"".concat(c,"-disabled"),a)),"aria-disabled":a},e9));var e5=(0,d.default)(c,S,(0,u.default)((0,u.default)((0,u.default)((0,u.default)((0,u.default)({},"".concat(c,"-start"),"start"===T),"".concat(c,"-center"),"center"===T),"".concat(c,"-end"),"end"===T),"".concat(c,"-simple"),U),"".concat(c,"-disabled"),G));return t.default.createElement("ul",(0,i.default)({className:e5,style:K,ref:el},eT),eP,e3,U?eF:eq,e9,t.default.createElement($,{locale:W,rootPrefixCls:c,disabled:G,selectPrefixCls:void 0===s?"rc-select":s,changeSize:function(e){var t=z(e,es,j),i=ep>t&&0!==t?t:ep;ed(e),ev(i),null==L||L(ep,e),eg(i),null==B||B(i,e)},pageSize:es,pageSizeOptions:Z,quickGo:ek?ez:null,goButton:eK,showSizeChanger:Q,sizeChangerRender:Y}))};var N=e.i(727214),w=e.i(242064),j=e.i(517455),I=e.i(150073),O=e.i(408850),M=e.i(327494),B=e.i(104458);e.i(296059);var D=e.i(915654),T=e.i(349942),P=e.i(517458),H=e.i(889943),A=e.i(183293),q=e.i(246422),R=e.i(838378);let _=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,P.initComponentToken)(e)),L=e=>(0,R.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,P.initInputToken)(e)),X=(0,q.genStyleHooks)("Pagination",e=>{let t=L(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,D.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,D.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,D.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,D.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,D.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,T.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,D.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,D.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,D.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,D.unit)(e.inputOutlineOffset)} 0 ${(0,D.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,D.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,D.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,D.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,T.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},_),W=(0,q.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(L(e)),_);function K(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var F=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:l,rootClassName:u,style:m,size:p,locale:g,responsive:f,showSizeChanger:b,selectComponentClass:h,pageSizeOptions:v}=e,S=F(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:$}=(0,I.default)(f),[,y]=(0,B.useToken)(),{getPrefixCls:C,direction:k,showSizeChanger:x,className:z,style:D}=(0,w.useComponentConfig)("pagination"),T=C("pagination",n),[P,H,A]=X(T),q=(0,j.default)(p),R="small"===q||!!($&&!q&&f),[_]=(0,O.useLocale)("Pagination",N.default),L=Object.assign(Object.assign({},_),g),[G,U]=K(b),[J,V]=K(x),Q=null!=U?U:V,Y=h||M.default,Z=t.useMemo(()=>v?v.map(e=>Number(e)):void 0,[v]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${T}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${T}-item-link`,type:"button",tabIndex:-1},"rtl"===k?t.createElement(s.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${T}-item-link`,type:"button",tabIndex:-1},"rtl"===k?t.createElement(c.default,null):t.createElement(s.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${T}-item-link`},t.createElement("div",{className:`${T}-item-container`},"rtl"===k?t.createElement(r,{className:`${T}-item-link-icon`}):t.createElement(a,{className:`${T}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${T}-item-link`},t.createElement("div",{className:`${T}-item-container`},"rtl"===k?t.createElement(a,{className:`${T}-item-link-icon`}):t.createElement(r,{className:`${T}-item-link-icon`}),e))}},[k,T]),et=C("select",o),ei=(0,d.default)({[`${T}-${i}`]:!!i,[`${T}-mini`]:R,[`${T}-rtl`]:"rtl"===k,[`${T}-bordered`]:y.wireframe},z,l,u,H,A),en=Object.assign(Object.assign({},D),m);return P(t.createElement(t.Fragment,null,y.wireframe&&t.createElement(W,{prefixCls:T}),t.createElement(E,Object.assign({},ee,S,{style:en,prefixCls:T,selectPrefixCls:et,className:ei,locale:L,pageSizeOptions:Z,showSizeChanger:null!=G?G:J,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:a,"aria-label":l,className:r,options:c}=e,{className:s,onChange:u}=Q||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":l,options:c},Q,{value:m,onChange:(e,t)=>{null==a||a(e),null==u||u(e,t)},size:R?"small":"middle",className:(0,d.default)(r,s)}))}}))))}],165370)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01ut.srbq8~b9.js b/litellm/proxy/_experimental/out/_next/static/chunks/01ut.srbq8~b9.js new file mode 100644 index 00000000000..cfc8e6ddd0d --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01ut.srbq8~b9.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,44121,186515,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 000 13.8z"}}]},name:"menu-fold",theme:"outlined"};var s=e.i(9583),r=a.forwardRef(function(e,r){return a.createElement(s.default,(0,t.default)({},e,{ref:r,icon:l}))});e.s(["MenuFoldOutlined",0,r],44121);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 000-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0014.4 7z"}}]},name:"menu-unfold",theme:"outlined"};var n=a.forwardRef(function(e,l){return a.createElement(s.default,(0,t.default)({},e,{ref:l,icon:i}))});e.s(["MenuUnfoldOutlined",0,n],186515)},251773,276701,771243,895335,e=>{"use strict";var t=e.i(843476),a=e.i(731565),l=e.i(602869),s=e.i(266027);async function r(){let e=(0,l.getProxyBaseUrl)(),t=await fetch(`${e}/public/litellm_blog_posts`);if(!t.ok)throw Error(`Failed to fetch blog posts: ${t.statusText}`);return t.json()}let i="inline-flex h-9 shrink-0 items-center justify-center gap-1 rounded-md px-2 text-sm font-medium leading-none text-gray-800 transition-colors hover:bg-gray-100 hover:text-gray-950";e.s(["NAV_PRODUCT_LINK_CLASS",0,i],276701);var n=e.i(755151),o=e.i(56456),c=e.i(464571),d=e.i(326373),m=e.i(770914),h=e.i(898586);let{Text:u,Title:g,Paragraph:x}=h.Typography;e.s(["BlogDropdown",0,()=>{let e,l=(0,a.useDisableBlogPosts)(),{data:h,isLoading:p,isError:f,refetch:b}=(0,s.useQuery)({queryKey:["blogPosts"],queryFn:r,staleTime:36e5,retry:1,retryDelay:0});return l?null:(e=p?[{key:"loading",label:(0,t.jsx)(o.LoadingOutlined,{}),disabled:!0}]:f?[{key:"error",label:(0,t.jsxs)(m.Space,{children:[(0,t.jsx)(u,{type:"danger",children:"Failed to load posts"}),(0,t.jsx)(c.Button,{size:"small",onClick:()=>b(),children:"Retry"})]}),disabled:!0}]:h&&0!==h.posts.length?[...h.posts.slice(0,5).map(e=>({key:e.url,label:(0,t.jsxs)("a",{href:e.url,target:"_blank",rel:"noopener noreferrer",style:{display:"block",width:380},children:[(0,t.jsx)(g,{level:5,style:{marginBottom:2},children:e.title}),(0,t.jsx)(u,{type:"secondary",style:{fontSize:11},children:new Date(e.date+"T00:00:00").toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}),(0,t.jsx)(x,{ellipsis:{rows:2},children:e.description})]})})),{type:"divider"},{key:"view-all",label:(0,t.jsx)("a",{href:"https://docs.litellm.ai/blog",target:"_blank",rel:"noopener noreferrer",children:"View all posts"})}]:[{key:"empty",label:(0,t.jsx)(u,{type:"secondary",children:"No posts available"}),disabled:!0}],(0,t.jsx)(d.Dropdown,{menu:{items:e},trigger:["hover"],placement:"bottomRight",children:(0,t.jsxs)(c.Button,{type:"text",className:`${i} border-0! bg-transparent!`,children:["Blog",(0,t.jsx)(n.DownOutlined,{className:"text-[10px] text-gray-500","aria-hidden":!0})]})}))}],251773);var p=e.i(636772);e.i(247167);var f=e.i(931067),b=e.i(271645);let y={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9a127.5 127.5 0 0138.1 91v112.5c.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z"}}]},name:"github",theme:"outlined"};var j=e.i(9583),v=b.forwardRef(function(e,t){return b.createElement(j.default,(0,f.default)({},e,{ref:t,icon:y}))});let w={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M409.4 128c-42.4 0-76.7 34.4-76.7 76.8 0 20.3 8.1 39.9 22.4 54.3a76.74 76.74 0 0054.3 22.5h76.7v-76.8c0-42.3-34.3-76.7-76.7-76.8zm0 204.8H204.7c-42.4 0-76.7 34.4-76.7 76.8s34.4 76.8 76.7 76.8h204.6c42.4 0 76.7-34.4 76.7-76.8.1-42.4-34.3-76.8-76.6-76.8zM614 486.4c42.4 0 76.8-34.4 76.7-76.8V204.8c0-42.4-34.3-76.8-76.7-76.8-42.4 0-76.7 34.4-76.7 76.8v204.8c0 42.5 34.3 76.8 76.7 76.8zm281.4-76.8c0-42.4-34.4-76.8-76.7-76.8S742 367.2 742 409.6v76.8h76.7c42.3 0 76.7-34.4 76.7-76.8zm-76.8 128H614c-42.4 0-76.7 34.4-76.7 76.8 0 20.3 8.1 39.9 22.4 54.3a76.74 76.74 0 0054.3 22.5h204.6c42.4 0 76.7-34.4 76.7-76.8.1-42.4-34.3-76.7-76.7-76.8zM614 742.4h-76.7v76.8c0 42.4 34.4 76.8 76.7 76.8 42.4 0 76.8-34.4 76.7-76.8.1-42.4-34.3-76.7-76.7-76.8zM409.4 537.6c-42.4 0-76.7 34.4-76.7 76.8v204.8c0 42.4 34.4 76.8 76.7 76.8 42.4 0 76.8-34.4 76.7-76.8V614.4c0-20.3-8.1-39.9-22.4-54.3a76.92 76.92 0 00-54.3-22.5zM128 614.4c0 20.3 8.1 39.9 22.4 54.3a76.74 76.74 0 0054.3 22.5c42.4 0 76.8-34.4 76.7-76.8v-76.8h-76.7c-42.3 0-76.7 34.4-76.7 76.8z"}}]},name:"slack",theme:"outlined"};var k=b.forwardRef(function(e,t){return b.createElement(j.default,(0,f.default)({},e,{ref:t,icon:w}))}),S=e.i(592968);let N="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer";e.s(["CommunityEngagementButtons",0,()=>(0,p.useDisableShowPrompts)()?null:(0,t.jsxs)("div",{className:"flex items-center gap-0.5 rounded-md border border-gray-200/80 bg-gray-50 px-0.5 py-0","aria-label":"Community links",children:[(0,t.jsx)(S.Tooltip,{title:"LiteLLM Slack community",children:(0,t.jsx)("a",{href:"https://www.litellm.ai/support",target:"_blank",rel:"noopener noreferrer",className:N,"aria-label":"Join Slack",children:(0,t.jsx)(k,{className:"text-lg"})})}),(0,t.jsx)(S.Tooltip,{title:"LiteLLM on GitHub",children:(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm",target:"_blank",rel:"noopener noreferrer",className:N,"aria-label":"LiteLLM on GitHub",children:(0,t.jsx)(v,{className:"text-lg"})})})]})],771243);var C=e.i(115571);let L="litellmHideAgentPlatformBanner";function B(e){let t=t=>{t.key===L&&e()},a=t=>{let{key:a}=t.detail;a===L&&e()};return window.addEventListener("storage",t),window.addEventListener(C.LOCAL_STORAGE_EVENT,a),()=>{window.removeEventListener("storage",t),window.removeEventListener(C.LOCAL_STORAGE_EVENT,a)}}function z(){return"true"===(0,C.getLocalStorageItem)(L)}let _={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M816 768h-24V428c0-141.1-104.3-257.7-240-277.1V112c0-22.1-17.9-40-40-40s-40 17.9-40 40v38.9c-135.7 19.4-240 136-240 277.1v340h-24c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h216c0 61.8 50.2 112 112 112s112-50.2 112-112h216c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM512 888c-26.5 0-48-21.5-48-48h96c0 26.5-21.5 48-48 48zM304 768V428c0-55.6 21.6-107.8 60.9-147.1S456.4 220 512 220c55.6 0 107.8 21.6 147.1 60.9S720 372.4 720 428v340H304z"}}]},name:"bell",theme:"outlined"};var I=b.forwardRef(function(e,t){return b.createElement(j.default,(0,f.default)({},e,{ref:t,icon:_}))}),A=e.i(906579),P=e.i(282786);e.s(["NotificationsBell",0,()=>{let e=!(0,b.useSyncExternalStore)(B,z),[a,l]=(0,b.useState)(!1),s=(0,t.jsxs)("div",{className:"max-w-[280px]",children:[(0,t.jsx)(h.Typography.Title,{level:5,className:"mt-0! mb-2!",children:"LiteLLM Agent Platform"}),(0,t.jsx)(h.Typography.Paragraph,{type:"secondary",className:"mb-3! text-sm leading-snug",children:"Open-source agent infra — sandboxes, durable sessions, and workers on AWS Fargate."}),(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsx)(c.Button,{type:"primary",size:"small",href:"https://github.com/BerriAI/litellm-agent-platform",target:"_blank",rel:"noopener noreferrer",children:"GitHub"}),e?(0,t.jsx)(c.Button,{type:"link",size:"small",className:"px-1!",onClick:()=>{(0,C.setLocalStorageItem)(L,"true"),(0,C.emitLocalStorageChange)(L),l(!1)},children:"Mark as read"}):null]})]});return(0,t.jsx)(P.Popover,{content:s,trigger:"click",open:a,onOpenChange:l,placement:"bottomRight",children:(0,t.jsx)(c.Button,{type:"text",className:"flex! h-9! w-9! items-center justify-center rounded-md! text-gray-600 transition-colors hover:bg-gray-100! hover:text-gray-900!","aria-label":"Notifications",children:(0,t.jsx)(A.Badge,{dot:e,color:"#1677ff",size:"small",offset:[8,2],children:(0,t.jsx)(I,{className:"text-base","aria-hidden":!0})})})})}],895335)},641141,e=>{"use strict";var t=e.i(843476),a=e.i(135214),l=e.i(731565),s=e.i(912089),r=e.i(636772),i=e.i(371401),n=e.i(115571),o=e.i(222038),c=e.i(100486),d=e.i(755151);e.i(247167);var m=e.i(931067),h=e.i(271645);let u={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z"}}]},name:"logout",theme:"outlined"};var g=e.i(9583),x=h.forwardRef(function(e,t){return h.createElement(g.default,(0,m.default)({},e,{ref:t,icon:u}))});let p={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 110.8V792H136V270.8l-27.6-21.5 39.3-50.5 42.8 33.3h643.1l42.8-33.3 39.3 50.5-27.7 21.5zM833.6 232L512 482 190.4 232l-42.8-33.3-39.3 50.5 27.6 21.5 341.6 265.6a55.99 55.99 0 0068.7 0L888 270.8l27.6-21.5-39.3-50.5-42.7 33.2z"}}]},name:"mail",theme:"outlined"};var f=h.forwardRef(function(e,t){return h.createElement(g.default,(0,m.default)({},e,{ref:t,icon:p}))}),b=e.i(602073),y=e.i(771674),j=e.i(464571),v=e.i(312361),w=e.i(326373),k=e.i(770914),S=e.i(790848),N=e.i(262218),C=e.i(592968),L=e.i(898586),B=e.i(344523),z=e.i(799676),_=e.i(115504);let{Text:I}=L.Typography;e.s(["default",0,({onLogout:e,variant:m="navbar",collapsed:u=!1})=>{let{userId:g,userEmail:p,userRole:L,premiumUser:A}=(0,a.default)(),P=(0,r.useDisableShowPrompts)(),T=(0,i.useDisableUsageIndicator)(),U=(0,l.useDisableBlogPosts)(),M=(0,s.useDisableBouncingIcon)(),[D,O]=(0,h.useState)(!1);(0,h.useEffect)(()=>{O("true"===(0,n.getLocalStorageItem)("disableShowNewBadge"))},[]);let H=[{key:"logout",label:(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(x,{}),"Logout"]}),onClick:e}],E=p||g||"user",R=function(e,t){let a=e?.split("@")[0]?.trim();if(a){let e=a.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean);if(e.length>=2)return`${e[0].charAt(0)}${e[1].charAt(0)}`.toUpperCase();if(1===e.length){let t=e[0];return t.length>=2?t.slice(0,2).toUpperCase():`${t.charAt(0)}`.toUpperCase()}}return t&&t.length>=2?t.slice(0,2).toUpperCase():t&&1===t.length?`${t.toUpperCase()}•`:"?"}(p,g),$=function(e){let t=0;for(let a=0;a(0,t.jsxs)("div",{className:"rounded-lg bg-white shadow-lg","data-testid":"user-dropdown-panel",children:[(0,t.jsxs)(k.Space,{direction:"vertical",size:"small",style:{width:"100%",padding:"12px"},children:[(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(f,{}),(0,t.jsx)(I,{type:"secondary",children:p||"-"})]}),A?(0,t.jsx)(N.Tag,{icon:(0,t.jsx)(c.CrownOutlined,{}),color:"gold",children:"Premium"}):(0,t.jsx)(C.Tooltip,{title:"Upgrade to Premium for advanced features",placement:"left",children:(0,t.jsx)(N.Tag,{icon:(0,t.jsx)(c.CrownOutlined,{}),children:"Standard"})})]}),(0,t.jsx)(v.Divider,{style:{margin:"8px 0"}}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(y.UserOutlined,{}),(0,t.jsx)(I,{type:"secondary",children:"User ID"})]}),(0,t.jsx)(I,{copyable:!0,ellipsis:!0,style:{maxWidth:"150px"},title:g||"-",children:g||"-"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(b.SafetyOutlined,{}),(0,t.jsx)(I,{type:"secondary",children:"Role"})]}),(0,t.jsx)(I,{children:L})]}),(0,t.jsx)(v.Divider,{style:{margin:"8px 0"}}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide New Feature Indicators"}),(0,t.jsx)(S.Switch,{size:"small",checked:D,onChange:e=>{O(e),e?(0,n.setLocalStorageItem)("disableShowNewBadge","true"):(0,n.removeLocalStorageItem)("disableShowNewBadge"),(0,n.emitLocalStorageChange)("disableShowNewBadge")},"aria-label":"Toggle hide new feature indicators"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide All Prompts"}),(0,t.jsx)(S.Switch,{size:"small",checked:P,onChange:e=>{e?(0,n.setLocalStorageItem)("disableShowPrompts","true"):(0,n.removeLocalStorageItem)("disableShowPrompts"),(0,n.emitLocalStorageChange)("disableShowPrompts")},"aria-label":"Toggle hide all prompts"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide Usage Indicator"}),(0,t.jsx)(S.Switch,{size:"small",checked:T,onChange:e=>{e?(0,n.setLocalStorageItem)("disableUsageIndicator","true"):(0,n.removeLocalStorageItem)("disableUsageIndicator"),(0,n.emitLocalStorageChange)("disableUsageIndicator")},"aria-label":"Toggle hide usage indicator"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide Blog Posts"}),(0,t.jsx)(S.Switch,{size:"small",checked:U,onChange:e=>{e?(0,n.setLocalStorageItem)("disableBlogPosts","true"):(0,n.removeLocalStorageItem)("disableBlogPosts"),(0,n.emitLocalStorageChange)("disableBlogPosts")},"aria-label":"Toggle hide blog posts"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide Bouncing Icon"}),(0,t.jsx)(S.Switch,{size:"small",checked:M,onChange:e=>{e?(0,n.setLocalStorageItem)("disableBouncingIcon","true"):(0,n.removeLocalStorageItem)("disableBouncingIcon"),(0,n.emitLocalStorageChange)("disableBouncingIcon")},"aria-label":"Toggle hide bouncing icon"})]})]}),(0,t.jsx)(v.Divider,{style:{margin:0}}),h.default.cloneElement(e,{style:{boxShadow:"none"}})]}),children:"sidebar"===m?(0,t.jsxs)("button",{type:"button",className:(0,_.cn)("flex w-full items-center rounded-lg border border-transparent transition-colors hover:bg-sidebar-accent",u?"justify-center px-0 py-1":"gap-2.5 px-2 py-1.5 text-left"),"aria-label":`Account menu — ${L??"Unknown role"} — signed in as ${p||g||"unknown"}`,"aria-haspopup":"menu",title:u?V:void 0,children:[(0,t.jsx)(z.Avatar,{className:"size-[30px] shadow-inner ring-1 ring-black/5","aria-hidden":!0,children:(0,t.jsx)(z.AvatarFallback,{className:"font-semibold text-white",style:{backgroundColor:`hsl(${$} 46% 38%)`},children:R})}),!u&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("span",{className:"min-w-0 flex-1 leading-tight",children:[(0,t.jsx)("span",{className:"block truncate text-[13px] font-medium text-sidebar-foreground",children:V}),L&&(0,t.jsx)("span",{className:"block truncate text-[11px] text-muted-foreground",children:L})]}),(0,t.jsx)(B.ChevronsUpDown,{size:16,strokeWidth:1.75,className:"shrink-0 text-muted-foreground","aria-hidden":!0})]})]}):(0,t.jsxs)(j.Button,{type:"text",className:"flex! max-w-[min(200px,34vw)] items-center gap-2 rounded-md! py-0.5! pl-1! pr-2! transition-colors hover:bg-gray-100!","aria-label":`Account menu — ${L??"Unknown role"} — signed in as ${p||g||"unknown"}`,"aria-haspopup":"menu",children:[(0,t.jsx)(z.Avatar,{className:"shadow-inner ring-1 ring-black/5","aria-hidden":!0,children:(0,t.jsx)(z.AvatarFallback,{className:"font-semibold text-white",style:{backgroundColor:`hsl(${$} 46% 38%)`},children:R})}),(0,t.jsx)("span",{className:"hidden min-w-0 truncate text-left text-sm font-medium leading-none text-gray-900 md:inline",children:V}),(0,t.jsx)(d.DownOutlined,{className:"hidden shrink-0 text-[10px] text-gray-400 md:inline","aria-hidden":!0})]})})}],641141)},853295,658140,383862,e=>{"use strict";var t=e.i(843476),a=e.i(618566),l=e.i(326373),s=e.i(477189),r=e.i(492030),i=e.i(344523),n=e.i(271645),o=e.i(431703),c=e.i(602869);let d=(0,n.createContext)({mode:"ai-gateway",setMode:()=>{},plugins:[],activePlugin:null}),m="litellm_plugin_mode",h=(0,o.createApiClient)({getBaseUrl:()=>(0,c.getProxyBaseUrl)()??""});function u(){return localStorage.getItem(m)??"ai-gateway"}function g(){return(0,n.useContext)(d)}e.s(["PluginModeProvider",0,function({children:e,accessToken:a}){let[l,s]=(0,n.useState)(u),[r,i]=(0,n.useState)([]),[o,c]=(0,n.useState)(!1);(0,n.useEffect)(()=>{a&&h.get("/api/plugins",{accessToken:a}).then(e=>{i(Array.isArray(e)?e:[])}).catch(()=>{}).finally(()=>c(!0))},[a]);let g="ai-gateway"!==l&&o&&!r.some(e=>e.name===l)?"ai-gateway":l,x=r.find(e=>e.name===g)??null;return(0,t.jsx)(d.Provider,{value:{mode:g,setMode:e=>{s(e),localStorage.setItem(m,e)},plugins:r,activePlugin:x},children:e})},"usePluginMode",0,g],658140);var x=e.i(292639),p=e.i(571353);let f="chat";e.s(["default",0,function(){let{mode:e,setMode:n,plugins:o}=g(),{data:c}=(0,x.useUISettings)(),d=(0,a.usePathname)(),m=!!c?.values?.enable_chat_ui,h=(0,p.migratedHref)(f),u=(d??"").replace(/\/+$/,""),b=m&&(u===h||u.startsWith(`${h}/`)),y=b?"Chat":o.find(t=>t.name===e)?.display_name??"AI Gateway",j=[{key:"ai-gateway",label:"AI Gateway"},...o.map(e=>({key:e.name,label:e.display_name}))],v=m?{key:f,label:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-6 py-0.5",children:[(0,t.jsx)("span",{className:"font-medium",children:"Chat"}),b&&(0,t.jsx)(r.CheckOutlined,{className:"text-blue-600"})]})}:{key:f,disabled:!0,label:(0,t.jsxs)("div",{className:"flex max-w-[220px] flex-col py-0.5",children:[(0,t.jsx)("span",{className:"font-medium",children:"Chat"}),(0,t.jsx)("span",{className:"whitespace-normal text-xs leading-snug text-muted-foreground",children:"Admins can enable in Settings"})]})},w=[...j.map(a=>({key:a.key,label:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-6 py-0.5",children:[(0,t.jsx)("span",{className:"font-medium",children:a.label}),!b&&a.key===e&&(0,t.jsx)(r.CheckOutlined,{className:"text-blue-600"})]})})),v];return(0,t.jsx)(l.Dropdown,{menu:{items:w,onClick:({key:e})=>{e===f?window.location.assign((0,p.migratedHref)(f)):(n(e),b&&window.location.assign((0,p.migratedHref)("")))},selectedKeys:[b?f:e]},trigger:["click"],children:(0,t.jsxs)("button",{type:"button",className:"flex h-8 max-w-[220px] items-center gap-1.5 rounded-md border border-border bg-background pl-1.5 pr-2 text-sm font-medium text-foreground transition-colors hover:bg-accent",children:[(0,t.jsx)("span",{className:"flex size-5 flex-none items-center justify-center rounded bg-muted text-muted-foreground",children:(0,t.jsx)(s.AppstoreOutlined,{className:"text-[13px]"})}),(0,t.jsx)("span",{className:"truncate",children:y}),(0,t.jsx)(i.ChevronsUpDown,{className:"size-3.5 flex-none text-muted-foreground"})]})})}],853295);var b=e.i(199133),y=e.i(295320),j=e.i(283713);e.s(["default",0,({onWorkerSwitch:e})=>{let{isControlPlane:a,selectedWorker:l,workers:s}=(0,j.useWorker)();return a&&l?(0,t.jsx)(b.Select,{showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),value:l.worker_id,style:{minWidth:180},suffixIcon:(0,t.jsx)(y.CloudServerOutlined,{}),options:s.map(e=>({label:e.name,value:e.worker_id,disabled:e.worker_id===l.worker_id})),onChange:t=>{e(t)}}):null}],383862)},402874,e=>{"use strict";var t=e.i(843476),a=e.i(143488),l=e.i(912089),s=e.i(636772),r=e.i(283713),i=e.i(602869),n=e.i(275144),o=e.i(268004),c=e.i(321836),d=e.i(592392),m=e.i(755151),h=e.i(44121),u=e.i(186515),g=e.i(262218),x=e.i(522016),p=e.i(251773),f=e.i(771243),b=e.i(276701),y=e.i(895335),j=e.i(641141),v=e.i(853295),w=e.i(383862);e.s(["default",0,({accessToken:e,isPublicPage:k=!1,sidebarCollapsed:S=!1,onToggleSidebar:N})=>{let C=(0,i.getProxyBaseUrl)(),L=(0,d.default)(e),{logoUrl:B}=(0,n.useTheme)(),{data:z}=(0,a.useHealthReadinessDetails)(e),_=z?.litellm_version,I=(0,l.useDisableBouncingIcon)(),A=(0,s.useDisableShowPrompts)(),{isControlPlane:P,selectedWorker:T}=(0,r.useWorker)(),U=P&&null!==T,M=B||`${C}/get_image`;return(0,t.jsx)("nav",{className:"sticky top-0 z-10 border-b border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)("div",{className:"flex h-14 items-center px-4",children:[(0,t.jsxs)("div",{className:"flex shrink-0 items-center",children:[N&&(0,t.jsx)("button",{onClick:N,className:"mr-2 flex h-9 w-9 items-center justify-center rounded-md text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900",title:S?"Expand sidebar":"Collapse sidebar",children:(0,t.jsx)("span",{className:"text-lg",children:S?(0,t.jsx)(u.MenuUnfoldOutlined,{}):(0,t.jsx)(h.MenuFoldOutlined,{})})}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(x.default,{href:C||"/",className:"flex items-center",children:(0,t.jsx)("div",{className:"relative",children:(0,t.jsx)("div",{className:"flex h-10 max-w-48 items-center justify-center overflow-hidden",children:(0,t.jsx)("img",{src:M,alt:"LiteLLM Brand",className:"h-auto max-h-full w-auto max-w-full object-contain"})})})}),_&&(0,t.jsxs)("div",{className:"relative",children:[!I&&(0,t.jsx)("span",{className:"absolute -left-2 -top-1 animate-bounce text-lg",style:{animationDuration:"2s"},title:"Thanks for using LiteLLM!",children:"🌑"}),(0,t.jsx)(g.Tag,{className:"relative z-10 cursor-pointer text-xs font-medium",children:(0,t.jsxs)("a",{href:"https://docs.litellm.ai/release_notes",target:"_blank",rel:"noopener noreferrer",className:"shrink-0",children:["v",_]})})]})]})]}),!k&&(0,t.jsx)("div",{className:"ml-4 flex shrink-0 items-center border-l border-gray-200 pl-4",children:(0,t.jsx)(v.default,{})}),(0,t.jsxs)("div",{className:"ml-auto flex min-w-0 flex-1 items-center justify-end gap-4",children:[U&&(0,t.jsx)("div",{className:"flex shrink-0 items-center",children:(0,t.jsx)(w.default,{onWorkerSwitch:e=>{(0,o.clearTokenCookies)(),(0,c.clearStoredReturnUrl)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=`/ui/login?worker=${encodeURIComponent(e)}`}})}),(0,t.jsxs)("nav",{"aria-label":"Product documentation",className:`flex min-w-0 items-center gap-2 ${U?"border-l border-gray-200 pl-4":""}`,children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/",target:"_blank",rel:"noopener noreferrer",className:b.NAV_PRODUCT_LINK_CLASS,children:["Docs",(0,t.jsx)(m.DownOutlined,{className:"pointer-events-none text-[10px] opacity-0","aria-hidden":!0})]}),(0,t.jsx)(p.BlogDropdown,{})]}),!A&&(0,t.jsx)("div",{className:"flex shrink-0 items-center border-l border-gray-200 pl-4",children:(0,t.jsx)(f.CommunityEngagementButtons,{})}),!k&&(0,t.jsx)("div",{className:"flex shrink-0 items-center border-l border-gray-200 pl-4",children:(0,t.jsxs)("div",{className:"flex items-center gap-0.5 rounded-lg bg-gray-50 px-1 py-0 transition-colors hover:bg-gray-100",children:[(0,t.jsx)(y.NotificationsBell,{}),(0,t.jsx)("span",{className:"mx-0.5 h-6 w-px shrink-0 bg-gray-200","aria-hidden":!0}),(0,t.jsx)(j.default,{onLogout:()=>{(0,o.clearTokenCookies)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=L.PROXY_LOGOUT_URL||""}})]})})]})]})})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01wjkyxc6hqho.js b/litellm/proxy/_experimental/out/_next/static/chunks/01wjkyxc6hqho.js deleted file mode 100644 index 0c6112b4cc7..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/01wjkyxc6hqho.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,94629,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,r],94629)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(829087),a=e.i(480731),s=e.i(444755),i=e.i(673706),o=e.i(95779);let n={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},m=(0,i.makeClassName)("Icon"),u=r.default.forwardRef((e,u)=>{let{icon:g,variant:x="simple",tooltip:h,size:p=a.Sizes.SM,color:b,className:f}=e,j=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),w=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,i.getColorClassNames)(t,o.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(x,b),{tooltipProps:v,getReferenceProps:C}=(0,l.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([u,v.refs.setReference]),className:(0,s.tremorTwMerge)(m("root"),"inline-flex shrink-0 items-center justify-center",w.bgColor,w.textColor,w.borderColor,w.ringColor,c[x].rounded,c[x].border,c[x].shadow,c[x].ring,n[p].paddingX,n[p].paddingY,f)},C,j),r.default.createElement(l.default,Object.assign({text:h},v)),r.default.createElement(g,{className:(0,s.tremorTwMerge)(m("icon"),"shrink-0",d[p].height,d[p].width)}))});u.displayName="Icon",e.s(["default",0,u],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("Table"),s=r.default.forwardRef((e,s)=>{let{children:i,className:o}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,l.tremorTwMerge)(a("root"),"overflow-auto",o)},r.default.createElement("table",Object.assign({ref:s,className:(0,l.tremorTwMerge)(a("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},n),i))});s.displayName="Table",e.s(["Table",0,s],269200)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableHead"),s=r.default.forwardRef((e,s)=>{let{children:i,className:o}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:s,className:(0,l.tremorTwMerge)(a("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",o)},n),i))});s.displayName="TableHead",e.s(["TableHead",0,s],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableHeaderCell"),s=r.default.forwardRef((e,s)=>{let{children:i,className:o}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:s,className:(0,l.tremorTwMerge)(a("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",o)},n),i))});s.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,s],64848)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableBody"),s=r.default.forwardRef((e,s)=>{let{children:i,className:o}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:s,className:(0,l.tremorTwMerge)(a("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",o)},n),i))});s.displayName="TableBody",e.s(["TableBody",0,s],942232)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableRow"),s=r.default.forwardRef((e,s)=>{let{children:i,className:o}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:s,className:(0,l.tremorTwMerge)(a("row"),o)},n),i))});s.displayName="TableRow",e.s(["TableRow",0,s],496020)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableCell"),s=r.default.forwardRef((e,s)=>{let{children:i,className:o}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:s,className:(0,l.tremorTwMerge)(a("root"),"align-middle whitespace-nowrap text-left p-4",o)},n),i))});s.displayName="TableCell",e.s(["TableCell",0,s],977572)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},250980,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,r],250980)},991124,e=>{"use strict";let t=(0,e.i(475254).default)("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);e.s(["default",0,t])},678745,e=>{"use strict";let t=(0,e.i(475254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,t])},678784,e=>{"use strict";var t=e.i(678745);e.s(["CheckIcon",()=>t.default])},118366,e=>{"use strict";var t=e.i(991124);e.s(["CopyIcon",()=>t.default])},601757,e=>{"use strict";var t=e.i(843476),r=e.i(271645),l=e.i(752978),a=e.i(994388),s=e.i(309426),i=e.i(599724),o=e.i(350967),n=e.i(278587),d=e.i(304967),c=e.i(629569),m=e.i(389083),u=e.i(677667),g=e.i(898667),x=e.i(130643),h=e.i(808613),p=e.i(311451),b=e.i(199133),f=e.i(592968),j=e.i(827252),w=e.i(702597),v=e.i(355619),C=e.i(602869),N=e.i(727749),y=e.i(435451),T=e.i(860585),k=e.i(500330),_=e.i(678784),I=e.i(118366),M=e.i(464571);let E=({tagId:e,onClose:l,accessToken:s,is_admin:o,editTag:n})=>{let[E]=h.Form.useForm(),[S,B]=(0,r.useState)(null),[R,L]=(0,r.useState)(n),[D,F]=(0,r.useState)([]),[A,P]=(0,r.useState)({}),O=async(e,t)=>{await (0,k.copyToClipboard)(e)&&(P(e=>({...e,[t]:!0})),setTimeout(()=>{P(e=>({...e,[t]:!1}))},2e3))},H=async()=>{if(s)try{let t=(await (0,C.tagInfoCall)(s,[e]))[e];t&&(B(t),n&&E.setFieldsValue({name:t.name,description:t.description,models:t.models,max_budget:t.litellm_budget_table?.max_budget,budget_duration:t.litellm_budget_table?.budget_duration}))}catch(e){console.error("Error fetching tag details:",e),N.default.fromBackend("Error fetching tag details: "+e)}};(0,r.useEffect)(()=>{H()},[e,s]),(0,r.useEffect)(()=>{s&&(0,w.fetchUserModels)("dummy-user","Admin",s,F)},[s]);let z=async e=>{if(s)try{await (0,C.tagUpdateCall)(s,{name:e.name,description:e.description,models:e.models,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,budget_duration:e.budget_duration}),N.default.success("Tag updated successfully"),L(!1),H()}catch(e){console.error("Error updating tag:",e),N.default.fromBackend("Error updating tag: "+e)}};return S?(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-6",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Button,{onClick:l,className:"mb-4",children:"← Back to Tags"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Tag Name:"}),(0,t.jsx)("span",{className:"font-mono px-2 py-1 bg-gray-100 rounded-sm text-sm border border-gray-200",children:S.name}),(0,t.jsx)(M.Button,{type:"text",size:"small",icon:A["tag-name"]?(0,t.jsx)(_.CheckIcon,{size:12}):(0,t.jsx)(I.CopyIcon,{size:12}),onClick:()=>O(S.name,"tag-name"),className:`transition-all duration-200 ${A["tag-name"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]}),(0,t.jsx)(i.Text,{className:"text-gray-500",children:S.description||"No description"})]}),o&&!R&&(0,t.jsx)(a.Button,{onClick:()=>L(!0),children:"Edit Tag"})]}),R?(0,t.jsx)(d.Card,{children:(0,t.jsxs)(h.Form,{form:E,onFinish:z,layout:"vertical",initialValues:S,children:[(0,t.jsx)(h.Form.Item,{label:"Tag Name",name:"name",rules:[{required:!0,message:"Please input a tag name"}],children:(0,t.jsx)(p.Input,{className:"rounded-md border-gray-300"})}),(0,t.jsx)(h.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(p.Input.TextArea,{rows:4})}),(0,t.jsx)(h.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Models",(0,t.jsx)(f.Tooltip,{title:"Select which models are allowed to process this type of data",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",children:(0,t.jsx)(b.Select,{mode:"multiple",placeholder:"Select Models",children:D.map(e=>(0,t.jsx)(b.Select.Option,{value:e,children:(0,v.getModelDisplayName)(e)},e))})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(g.AccordionHeader,{children:(0,t.jsx)(c.Title,{className:"m-0",children:"Budget & Rate Limits"})}),(0,t.jsxs)(x.AccordionBody,{children:[(0,t.jsx)(h.Form.Item,{label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(f.Tooltip,{title:"Maximum amount in USD this tag can spend",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",children:(0,t.jsx)(y.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(h.Form.Item,{label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(f.Tooltip,{title:"How often the budget should reset",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",children:(0,t.jsx)(T.default,{onChange:e=>E.setFieldValue("budget_duration",e)})}),(0,t.jsx)("div",{className:"mt-4 p-3 bg-gray-50 rounded-md border border-gray-200",children:(0,t.jsxs)("p",{className:"text-sm text-gray-600",children:["TPM/RPM limits for tags are not currently supported. If you need this feature, please"," ",(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm/issues/new",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"create a GitHub issue"}),"."]})})]})]}),(0,t.jsxs)("div",{className:"flex justify-end space-x-2",children:[(0,t.jsx)(a.Button,{onClick:()=>L(!1),children:"Cancel"}),(0,t.jsx)(a.Button,{type:"submit",children:"Save Changes"})]})]})}):(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)(d.Card,{children:[(0,t.jsx)(c.Title,{children:"Tag Details"}),(0,t.jsxs)("div",{className:"space-y-4 mt-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Name"}),(0,t.jsx)(i.Text,{children:S.name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Description"}),(0,t.jsx)(i.Text,{children:S.description||"-"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Allowed Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-2",children:S.models&&0!==S.models.length?S.models.map(e=>(0,t.jsx)(m.Badge,{color:"blue",children:(0,t.jsx)(f.Tooltip,{title:`ID: ${e}`,children:S.model_info?.[e]||e})},e)):(0,t.jsx)(m.Badge,{color:"red",children:"All Models"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Created"}),(0,t.jsx)(i.Text,{children:S.created_at?new Date(S.created_at).toLocaleString():"-"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Last Updated"}),(0,t.jsx)(i.Text,{children:S.updated_at?new Date(S.updated_at).toLocaleString():"-"})]})]})]}),S.litellm_budget_table&&(0,t.jsxs)(d.Card,{children:[(0,t.jsx)(c.Title,{children:"Budget & Rate Limits"}),(0,t.jsxs)("div",{className:"space-y-4 mt-4",children:[void 0!==S.litellm_budget_table.max_budget&&null!==S.litellm_budget_table.max_budget&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Max Budget"}),(0,t.jsxs)(i.Text,{children:["$",S.litellm_budget_table.max_budget]})]}),S.litellm_budget_table.budget_duration&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Budget Duration"}),(0,t.jsx)(i.Text,{children:S.litellm_budget_table.budget_duration})]}),void 0!==S.litellm_budget_table.tpm_limit&&null!==S.litellm_budget_table.tpm_limit&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"TPM Limit"}),(0,t.jsx)(i.Text,{children:S.litellm_budget_table.tpm_limit.toLocaleString()})]}),void 0!==S.litellm_budget_table.rpm_limit&&null!==S.litellm_budget_table.rpm_limit&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"RPM Limit"}),(0,t.jsx)(i.Text,{children:S.litellm_budget_table.rpm_limit.toLocaleString()})]})]})]})]})]}):(0,t.jsx)("div",{children:"Loading..."})};var S=e.i(871943),B=e.i(360820),R=e.i(591935),L=e.i(94629),D=e.i(68155),F=e.i(152990),A=e.i(682830),P=e.i(269200),O=e.i(942232),H=e.i(977572),z=e.i(427612),U=e.i(64848),V=e.i(496020);let W="This is just a spend tag that was passed dynamically in a request. It does not control any LLM models.",Y=({data:e,onEdit:s,onDelete:o,onSelectTag:n})=>{let[d,c]=r.default.useState([{id:"created_at",desc:!0}]),u=[{header:"Tag Name",accessorKey:"name",cell:({row:e})=>{let r=e.original,l=r.description===W;return(0,t.jsx)("div",{className:"overflow-hidden",children:(0,t.jsx)(f.Tooltip,{title:l?"You cannot view the information of a dynamically generated spend tag":r.name,children:(0,t.jsx)(a.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5",onClick:()=>n(r.name),disabled:l,children:r.name})})})}},{header:"Description",accessorKey:"description",cell:({row:e})=>{let r=e.original;return(0,t.jsx)(f.Tooltip,{title:r.description,children:(0,t.jsx)("span",{className:"text-xs",children:r.description||"-"})})}},{header:"Allowed Models",accessorKey:"models",cell:({row:e})=>{let r=e.original;return(0,t.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:r?.models?.length===0?(0,t.jsx)(m.Badge,{size:"xs",className:"mb-1",color:"red",children:"All Models"}):r?.models?.map(e=>(0,t.jsx)(m.Badge,{size:"xs",className:"mb-1",color:"blue",children:(0,t.jsx)(f.Tooltip,{title:`ID: ${e}`,children:(0,t.jsx)(i.Text,{children:r.model_info?.[e]||e})})},e))})}},{header:"Created",accessorKey:"created_at",sortingFn:"datetime",cell:({row:e})=>{let r=e.original;return(0,t.jsx)("span",{className:"text-xs",children:new Date(r.created_at).toLocaleDateString()})}},{id:"actions",header:"Actions",cell:({row:e})=>{let r=e.original,a=r.description===W;return(0,t.jsxs)("div",{className:"flex space-x-2",children:[a?(0,t.jsx)(f.Tooltip,{title:"Dynamically generated spend tags cannot be edited",children:(0,t.jsx)(l.Icon,{icon:R.PencilAltIcon,size:"sm",className:"opacity-50 cursor-not-allowed","aria-label":"Edit tag (disabled)"})}):(0,t.jsx)(f.Tooltip,{title:"Edit tag",children:(0,t.jsx)(l.Icon,{icon:R.PencilAltIcon,size:"sm",onClick:()=>s(r),className:"cursor-pointer hover:text-blue-500"})}),a?(0,t.jsx)(f.Tooltip,{title:"Dynamically generated spend tags cannot be deleted",children:(0,t.jsx)(l.Icon,{icon:D.TrashIcon,size:"sm",className:"opacity-50 cursor-not-allowed","aria-label":"Delete tag (disabled)"})}):(0,t.jsx)(f.Tooltip,{title:"Delete tag",children:(0,t.jsx)(l.Icon,{icon:D.TrashIcon,size:"sm",onClick:()=>o(r.name),className:"cursor-pointer hover:text-red-500"})})]})}}],g=(0,F.useReactTable)({data:e,columns:u,state:{sorting:d},onSortingChange:c,getCoreRowModel:(0,A.getCoreRowModel)(),getSortedRowModel:(0,A.getSortedRowModel)(),enableSorting:!0});return(0,t.jsx)("div",{className:"rounded-lg custom-border relative",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(P.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(z.TableHead,{children:g.getHeaderGroups().map(e=>(0,t.jsx)(V.TableRow,{children:e.headers.map(e=>(0,t.jsx)(U.TableHeaderCell,{className:`py-1 h-8 ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,onClick:e.column.getToggleSortingHandler(),children:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,t.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,F.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&(0,t.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,t.jsx)(B.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,t.jsx)(S.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,t.jsx)(L.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]})},e.id))},e.id))}),(0,t.jsx)(O.TableBody,{children:g.getRowModel().rows.length>0?g.getRowModel().rows.map(e=>(0,t.jsx)(V.TableRow,{className:"h-8",children:e.getVisibleCells().map(e=>(0,t.jsx)(H.TableCell,{className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,children:(0,F.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,t.jsx)(V.TableRow,{children:(0,t.jsx)(H.TableCell,{colSpan:u.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"No tags found"})})})})})]})})})};var q=e.i(779241),K=e.i(212931);let X=({visible:e,onCancel:r,onSubmit:l,availableModels:s})=>{let[i]=h.Form.useForm();return(0,t.jsx)(K.Modal,{title:"Create New Tag",open:e,width:800,footer:null,onCancel:()=>{i.resetFields(),r()},children:(0,t.jsxs)(h.Form,{form:i,onFinish:e=>{l(e),i.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsx)(h.Form.Item,{label:"Tag Name",name:"tag_name",rules:[{required:!0,message:"Please input a tag name"}],children:(0,t.jsx)(q.TextInput,{})}),(0,t.jsx)(h.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(p.Input.TextArea,{rows:4})}),(0,t.jsx)(h.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Models",(0,t.jsx)(f.Tooltip,{title:"Select which models are allowed to process requests from this tag",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_llms",children:(0,t.jsx)(b.Select,{mode:"multiple",placeholder:"Select Models",children:s.map(e=>(0,t.jsx)(b.Select.Option,{value:e.model_info.id,children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{children:e.model_name}),(0,t.jsxs)("span",{className:"text-gray-400 ml-2",children:["(",e.model_info.id,")"]})]})},e.model_info.id))})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(g.AccordionHeader,{children:(0,t.jsx)(c.Title,{className:"m-0",children:"Budget & Rate Limits (Optional)"})}),(0,t.jsxs)(x.AccordionBody,{children:[(0,t.jsx)(h.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(f.Tooltip,{title:"Maximum amount in USD this tag can spend. When reached, requests with this tag will be blocked",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",children:(0,t.jsx)(y.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(h.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(f.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",children:(0,t.jsx)(T.default,{onChange:e=>i.setFieldValue("budget_duration",e)})}),(0,t.jsx)("div",{className:"mt-4 p-3 bg-gray-50 rounded-md border border-gray-200",children:(0,t.jsxs)("p",{className:"text-sm text-gray-600",children:["TPM/RPM limits for tags are not currently supported. If you need this feature, please"," ",(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm/issues/new",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"create a GitHub issue"}),"."]})})]})]}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(a.Button,{type:"submit",children:"Create Tag"})})]})})},$=({accessToken:e,userID:d,userRole:c})=>{let[m,u]=(0,r.useState)([]),[g,x]=(0,r.useState)(!1),[h,p]=(0,r.useState)(null),[b,f]=(0,r.useState)(!1),[j,w]=(0,r.useState)(!1),[v,y]=(0,r.useState)(null),[T,k]=(0,r.useState)(""),[_,I]=(0,r.useState)([]),M=async()=>{if(e)try{let t=await (0,C.tagListCall)(e);u(Object.values(t))}catch(e){console.error("Error fetching tags:",e),N.default.fromBackend("Error fetching tags: "+e)}},S=async t=>{if(e)try{await (0,C.tagCreateCall)(e,{name:t.tag_name,description:t.description,models:t.allowed_llms,max_budget:t.max_budget,soft_budget:t.soft_budget,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,budget_duration:t.budget_duration}),N.default.success("Tag created successfully"),x(!1),M()}catch(e){console.error("Error creating tag:",e),N.default.fromBackend("Error creating tag: "+e)}},B=async e=>{y(e),w(!0)},R=async()=>{if(e&&v){try{await (0,C.tagDeleteCall)(e,v),N.default.success("Tag deleted successfully"),M()}catch(e){console.error("Error deleting tag:",e),N.default.fromBackend("Error deleting tag: "+e)}w(!1),y(null)}};return(0,r.useEffect)(()=>{d&&c&&e&&(async()=>{try{let t=await (0,C.modelInfoCall)(e,d,c);t&&t.data&&I(t.data)}catch(e){console.error("Error fetching models:",e),N.default.fromBackend("Error fetching models: "+e)}})()},[e,d,c]),(0,r.useEffect)(()=>{M()},[e]),(0,t.jsx)("div",{className:"w-full mx-4 h-[75vh]",children:h?(0,t.jsx)(E,{tagId:h,onClose:()=>{p(null),f(!1)},accessToken:e,is_admin:"Admin"===c,editTag:b}):(0,t.jsxs)("div",{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,t.jsxs)("div",{className:"flex justify-between mt-2 w-full items-center mb-4",children:[(0,t.jsx)("h1",{children:"Tag Management"}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[T&&(0,t.jsxs)(i.Text,{children:["Last Refreshed: ",T]}),(0,t.jsx)(l.Icon,{icon:n.RefreshIcon,variant:"shadow",size:"xs",className:"self-center cursor-pointer",onClick:()=>{M(),k(new Date().toLocaleString())}})]})]}),(0,t.jsxs)(i.Text,{className:"mb-4",children:["Click on a tag name to view and edit its details.",(0,t.jsxs)("p",{children:["You can use tags to restrict the usage of certain LLMs based on tags passed in the request. Read more about tag routing"," ",(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/tag_routing",target:"_blank",rel:"noopener noreferrer",children:"here"}),"."]})]}),(0,t.jsx)(a.Button,{className:"mb-4",onClick:()=>x(!0),children:"+ Create New Tag"}),(0,t.jsx)(o.Grid,{numItems:1,className:"gap-2 pt-2 pb-2 h-[75vh] w-full mt-2",children:(0,t.jsx)(s.Col,{numColSpan:1,children:(0,t.jsx)(Y,{data:m,onEdit:e=>{p(e.name),f(!0)},onDelete:B,onSelectTag:p})})}),(0,t.jsx)(X,{visible:g,onCancel:()=>x(!1),onSubmit:S,availableModels:_}),j&&(0,t.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,t.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,t.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,t.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,t.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,t.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,t.jsx)("div",{className:"sm:flex sm:items-start",children:(0,t.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,t.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Tag"}),(0,t.jsx)("div",{className:"mt-2",children:(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this tag?"})})]})})}),(0,t.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,t.jsx)(a.Button,{onClick:R,color:"red",className:"ml-2",children:"Delete"}),(0,t.jsx)(a.Button,{onClick:()=>{w(!1),y(null)},children:"Cancel"})]})]})]})})]})})};var G=e.i(135214);e.s(["default",0,function(){let{accessToken:e,userRole:r,userId:l}=(0,G.default)();return(0,t.jsx)($,{accessToken:e,userRole:r,userID:l})}],601757)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02-2~p5k.ielz.js b/litellm/proxy/_experimental/out/_next/static/chunks/02-2~p5k.ielz.js new file mode 100644 index 00000000000..b18990d8cf2 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02-2~p5k.ielz.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,312361,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(242064),i=e.i(517455);e.i(296059);var a=e.i(915654),l=e.i(183293),o=e.i(246422),c=e.i(838378);let s=(0,o.genStyleHooks)("Divider",e=>{let t=(0,c.mergeToken)(e,{dividerHorizontalWithTextGutterMargin:e.margin,sizePaddingEdgeHorizontal:0});return[(e=>{let{componentCls:t,sizePaddingEdgeHorizontal:n,colorSplit:r,lineWidth:i,textPaddingInline:o,orientationMargin:c,verticalMarginInline:s}=e;return{[t]:Object.assign(Object.assign({},(0,l.resetComponent)(e)),{borderBlockStart:`${(0,a.unit)(i)} solid ${r}`,"&-vertical":{position:"relative",top:"-0.06em",display:"inline-block",height:"0.9em",marginInline:s,marginBlock:0,verticalAlign:"middle",borderTop:0,borderInlineStart:`${(0,a.unit)(i)} solid ${r}`},"&-horizontal":{display:"flex",clear:"both",width:"100%",minWidth:"100%",margin:`${(0,a.unit)(e.marginLG)} 0`},[`&-horizontal${t}-with-text`]:{display:"flex",alignItems:"center",margin:`${(0,a.unit)(e.dividerHorizontalWithTextGutterMargin)} 0`,color:e.colorTextHeading,fontWeight:500,fontSize:e.fontSizeLG,whiteSpace:"nowrap",textAlign:"center",borderBlockStart:`0 ${r}`,"&::before, &::after":{position:"relative",width:"50%",borderBlockStart:`${(0,a.unit)(i)} solid transparent`,borderBlockStartColor:"inherit",borderBlockEnd:0,transform:"translateY(50%)",content:"''"}},[`&-horizontal${t}-with-text-start`]:{"&::before":{width:`calc(${c} * 100%)`},"&::after":{width:`calc(100% - ${c} * 100%)`}},[`&-horizontal${t}-with-text-end`]:{"&::before":{width:`calc(100% - ${c} * 100%)`},"&::after":{width:`calc(${c} * 100%)`}},[`${t}-inner-text`]:{display:"inline-block",paddingBlock:0,paddingInline:o},"&-dashed":{background:"none",borderColor:r,borderStyle:"dashed",borderWidth:`${(0,a.unit)(i)} 0 0`},[`&-horizontal${t}-with-text${t}-dashed`]:{"&::before, &::after":{borderStyle:"dashed none none"}},[`&-vertical${t}-dashed`]:{borderInlineStartWidth:i,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},"&-dotted":{background:"none",borderColor:r,borderStyle:"dotted",borderWidth:`${(0,a.unit)(i)} 0 0`},[`&-horizontal${t}-with-text${t}-dotted`]:{"&::before, &::after":{borderStyle:"dotted none none"}},[`&-vertical${t}-dotted`]:{borderInlineStartWidth:i,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},[`&-plain${t}-with-text`]:{color:e.colorText,fontWeight:"normal",fontSize:e.fontSize},[`&-horizontal${t}-with-text-start${t}-no-default-orientation-margin-start`]:{"&::before":{width:0},"&::after":{width:"100%"},[`${t}-inner-text`]:{paddingInlineStart:n}},[`&-horizontal${t}-with-text-end${t}-no-default-orientation-margin-end`]:{"&::before":{width:"100%"},"&::after":{width:0},[`${t}-inner-text`]:{paddingInlineEnd:n}}})}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-horizontal":{[`&${t}`]:{"&-sm":{marginBlock:e.marginXS},"&-md":{marginBlock:e.margin}}}}}})(t)]},e=>({textPaddingInline:"1em",orientationMargin:.05,verticalMarginInline:e.marginXS}),{unitless:{orientationMargin:!0}});var d=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let u={small:"sm",middle:"md"};e.s(["Divider",0,e=>{let{getPrefixCls:a,direction:l,className:o,style:c}=(0,r.useComponentConfig)("divider"),{prefixCls:g,type:p="horizontal",orientation:m="center",orientationMargin:f,className:b,rootClassName:h,children:$,dashed:y,variant:v="solid",plain:S,style:C,size:k}=e,w=d(e,["prefixCls","type","orientation","orientationMargin","className","rootClassName","children","dashed","variant","plain","style","size"]),O=a("divider",g),[x,I,E]=s(O),j=u[(0,i.default)(k)],z=!!$,N=t.useMemo(()=>"left"===m?"rtl"===l?"end":"start":"right"===m?"rtl"===l?"start":"end":m,[l,m]),P="start"===N&&null!=f,M="end"===N&&null!=f,T=(0,n.default)(O,o,I,E,`${O}-${p}`,{[`${O}-with-text`]:z,[`${O}-with-text-${N}`]:z,[`${O}-dashed`]:!!y,[`${O}-${v}`]:"solid"!==v,[`${O}-plain`]:!!S,[`${O}-rtl`]:"rtl"===l,[`${O}-no-default-orientation-margin-start`]:P,[`${O}-no-default-orientation-margin-end`]:M,[`${O}-${j}`]:!!j},b,h),B=t.useMemo(()=>"number"==typeof f?f:/^\d+$/.test(f)?Number(f):f,[f]);return x(t.createElement("div",Object.assign({className:T,style:Object.assign(Object.assign({},c),C)},w,{role:"separator"}),$&&"vertical"!==p&&t.createElement("span",{className:`${O}-inner-text`,style:{marginInlineStart:P?B:void 0,marginInlineEnd:M?B:void 0}},$)))}],312361)},56456,e=>{"use strict";var t=e.i(739295);e.s(["LoadingOutlined",()=>t.default])},801312,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"}}]},name:"left",theme:"outlined"};var i=e.i(9583),a=n.forwardRef(function(e,a){return n.createElement(i.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["default",0,a],801312)},38243,908286,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(876556);function i(e){return["small","middle","large"].includes(e)}function a(e){return!!e&&"number"==typeof e&&!Number.isNaN(e)}e.s(["isPresetSize",0,i,"isValidGapNumber",0,a],908286);var l=e.i(242064),o=e.i(249616),c=e.i(372409),s=e.i(246422);let d=(0,s.genStyleHooks)(["Space","Addon"],e=>[(e=>{let{componentCls:t,borderRadius:n,paddingSM:r,colorBorder:i,paddingXS:a,fontSizeLG:l,fontSizeSM:o,borderRadiusLG:s,borderRadiusSM:d,colorBgContainerDisabled:u,lineWidth:g}=e;return{[t]:[{display:"inline-flex",alignItems:"center",gap:0,paddingInline:r,margin:0,background:u,borderWidth:g,borderStyle:"solid",borderColor:i,borderRadius:n,"&-large":{fontSize:l,borderRadius:s},"&-small":{paddingInline:a,borderRadius:d,fontSize:o},"&-compact-last-item":{borderEndStartRadius:0,borderStartStartRadius:0},"&-compact-first-item":{borderEndEndRadius:0,borderStartEndRadius:0},"&-compact-item:not(:first-child):not(:last-child)":{borderRadius:0},"&-compact-item:not(:last-child)":{borderInlineEndWidth:0}},(0,c.genCompactItemStyle)(e,{focus:!1})]}})(e)]);var u=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let g=t.default.forwardRef((e,r)=>{let{className:i,children:a,style:c,prefixCls:s}=e,g=u(e,["className","children","style","prefixCls"]),{getPrefixCls:p,direction:m}=t.default.useContext(l.ConfigContext),f=p("space-addon",s),[b,h,$]=d(f),{compactItemClassnames:y,compactSize:v}=(0,o.useCompactItemContext)(f,m),S=(0,n.default)(f,h,y,$,{[`${f}-${v}`]:v},i);return b(t.default.createElement("div",Object.assign({ref:r,className:S,style:c},g),a))}),p=t.default.createContext({latestIndex:0}),m=p.Provider,f=({className:e,index:n,children:r,split:i,style:a})=>{let{latestIndex:l}=t.useContext(p);return null==r?null:t.createElement(t.Fragment,null,t.createElement("div",{className:e,style:a},r),n{let t=(0,b.mergeToken)(e,{spaceGapSmallSize:e.paddingXS,spaceGapMiddleSize:e.padding,spaceGapLargeSize:e.paddingLG});return[(e=>{let{componentCls:t,antCls:n}=e;return{[t]:{display:"inline-flex","&-rtl":{direction:"rtl"},"&-vertical":{flexDirection:"column"},"&-align":{flexDirection:"column","&-center":{alignItems:"center"},"&-start":{alignItems:"flex-start"},"&-end":{alignItems:"flex-end"},"&-baseline":{alignItems:"baseline"}},[`${t}-item:empty`]:{display:"none"},[`${t}-item > ${n}-badge-not-a-wrapper:only-child`]:{display:"block"}}}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-row-small":{rowGap:e.spaceGapSmallSize},"&-gap-row-middle":{rowGap:e.spaceGapMiddleSize},"&-gap-row-large":{rowGap:e.spaceGapLargeSize},"&-gap-col-small":{columnGap:e.spaceGapSmallSize},"&-gap-col-middle":{columnGap:e.spaceGapMiddleSize},"&-gap-col-large":{columnGap:e.spaceGapLargeSize}}}})(t)]},()=>({}),{resetStyle:!1});var $=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let y=t.forwardRef((e,o)=>{var c;let{getPrefixCls:s,direction:d,size:u,className:g,style:p,classNames:b,styles:y}=(0,l.useComponentConfig)("space"),{size:v=null!=u?u:"small",align:S,className:C,rootClassName:k,children:w,direction:O="horizontal",prefixCls:x,split:I,style:E,wrap:j=!1,classNames:z,styles:N}=e,P=$(e,["size","align","className","rootClassName","children","direction","prefixCls","split","style","wrap","classNames","styles"]),[M,T]=Array.isArray(v)?v:[v,v],B=i(T),H=i(M),R=a(T),W=a(M),D=(0,r.default)(w,{keepEmpty:!0}),q=void 0===S&&"horizontal"===O?"center":S,G=s("space",x),[L,A,F]=h(G),X=(0,n.default)(G,g,A,`${G}-${O}`,{[`${G}-rtl`]:"rtl"===d,[`${G}-align-${q}`]:q,[`${G}-gap-row-${T}`]:B,[`${G}-gap-col-${M}`]:H},C,k,F),V=(0,n.default)(`${G}-item`,null!=(c=null==z?void 0:z.item)?c:b.item),K=Object.assign(Object.assign({},y.item),null==N?void 0:N.item),U=D.map((e,n)=>{let r=(null==e?void 0:e.key)||`${V}-${n}`;return t.createElement(f,{className:V,key:r,index:n,split:I,style:K},e)}),_=t.useMemo(()=>({latestIndex:D.reduce((e,t,n)=>null!=t?n:e,0)}),[D]);if(0===D.length)return null;let Q={};return j&&(Q.flexWrap="wrap"),!H&&W&&(Q.columnGap=M),!B&&R&&(Q.rowGap=T),L(t.createElement("div",Object.assign({ref:o,className:X,style:Object.assign(Object.assign(Object.assign({},Q),p),E)},P),t.createElement(m,{value:_},U)))});y.Compact=o.default,y.Addon=g,e.s(["default",0,y],38243)},262218,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(529681),i=e.i(702779),a=e.i(563113),l=e.i(763731),o=e.i(121872),c=e.i(242064);e.i(296059);var s=e.i(915654),d=e.i(135551),u=e.i(183293),g=e.i(246422),p=e.i(838378);let m=e=>{let{lineWidth:t,fontSizeIcon:n,calc:r}=e,i=e.fontSizeSM;return(0,p.mergeToken)(e,{tagFontSize:i,tagLineHeight:(0,s.unit)(r(e.lineHeightSM).mul(i).equal()),tagIconSize:r(n).sub(r(t).mul(2)).equal(),tagPaddingHorizontal:8,tagBorderlessBg:e.defaultBg})},f=e=>({defaultBg:new d.FastColor(e.colorFillQuaternary).onBackground(e.colorBgContainer).toHexString(),defaultColor:e.colorText}),b=(0,g.genStyleHooks)("Tag",e=>(e=>{let{paddingXXS:t,lineWidth:n,tagPaddingHorizontal:r,componentCls:i,calc:a}=e,l=a(r).sub(n).equal(),o=a(t).sub(n).equal();return{[i]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{display:"inline-block",height:"auto",marginInlineEnd:e.marginXS,paddingInline:l,fontSize:e.tagFontSize,lineHeight:e.tagLineHeight,whiteSpace:"nowrap",background:e.defaultBg,border:`${(0,s.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusSM,opacity:1,transition:`all ${e.motionDurationMid}`,textAlign:"start",position:"relative",[`&${i}-rtl`]:{direction:"rtl"},"&, a, a:hover":{color:e.defaultColor},[`${i}-close-icon`]:{marginInlineStart:o,fontSize:e.tagIconSize,color:e.colorIcon,cursor:"pointer",transition:`all ${e.motionDurationMid}`,"&:hover":{color:e.colorTextHeading}},[`&${i}-has-color`]:{borderColor:"transparent",[`&, a, a:hover, ${e.iconCls}-close, ${e.iconCls}-close:hover`]:{color:e.colorTextLightSolid}},"&-checkable":{backgroundColor:"transparent",borderColor:"transparent",cursor:"pointer",[`&:not(${i}-checkable-checked):hover`]:{color:e.colorPrimary,backgroundColor:e.colorFillSecondary},"&:active, &-checked":{color:e.colorTextLightSolid},"&-checked":{backgroundColor:e.colorPrimary,"&:hover":{backgroundColor:e.colorPrimaryHover}},"&:active":{backgroundColor:e.colorPrimaryActive}},"&-hidden":{display:"none"},[`> ${e.iconCls} + span, > span + ${e.iconCls}`]:{marginInlineStart:l}}),[`${i}-borderless`]:{borderColor:"transparent",background:e.tagBorderlessBg}}})(m(e)),f);var h=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let $=t.forwardRef((e,r)=>{let{prefixCls:i,style:a,className:l,checked:o,children:s,icon:d,onChange:u,onClick:g}=e,p=h(e,["prefixCls","style","className","checked","children","icon","onChange","onClick"]),{getPrefixCls:m,tag:f}=t.useContext(c.ConfigContext),$=m("tag",i),[y,v,S]=b($),C=(0,n.default)($,`${$}-checkable`,{[`${$}-checkable-checked`]:o},null==f?void 0:f.className,l,v,S);return y(t.createElement("span",Object.assign({},p,{ref:r,style:Object.assign(Object.assign({},a),null==f?void 0:f.style),className:C,onClick:e=>{null==u||u(!o),null==g||g(e)}}),d,t.createElement("span",null,s)))});var y=e.i(403541);let v=(0,g.genSubStyleComponent)(["Tag","preset"],e=>{let t;return t=m(e),(0,y.genPresetColor)(t,(e,{textColor:n,lightBorderColor:r,lightColor:i,darkColor:a})=>({[`${t.componentCls}${t.componentCls}-${e}`]:{color:n,background:i,borderColor:r,"&-inverse":{color:t.colorTextLightSolid,background:a,borderColor:a},[`&${t.componentCls}-borderless`]:{borderColor:"transparent"}}}))},f),S=(e,t,n)=>{let r="string"!=typeof n?n:n.charAt(0).toUpperCase()+n.slice(1);return{[`${e.componentCls}${e.componentCls}-${t}`]:{color:e[`color${n}`],background:e[`color${r}Bg`],borderColor:e[`color${r}Border`],[`&${e.componentCls}-borderless`]:{borderColor:"transparent"}}}},C=(0,g.genSubStyleComponent)(["Tag","status"],e=>{let t=m(e);return[S(t,"success","Success"),S(t,"processing","Info"),S(t,"error","Error"),S(t,"warning","Warning")]},f);var k=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let w=t.forwardRef((e,s)=>{let{prefixCls:d,className:u,rootClassName:g,style:p,children:m,icon:f,color:h,onClose:$,bordered:y=!0,visible:S}=e,w=k(e,["prefixCls","className","rootClassName","style","children","icon","color","onClose","bordered","visible"]),{getPrefixCls:O,direction:x,tag:I}=t.useContext(c.ConfigContext),[E,j]=t.useState(!0),z=(0,r.default)(w,["closeIcon","closable"]);t.useEffect(()=>{void 0!==S&&j(S)},[S]);let N=(0,i.isPresetColor)(h),P=(0,i.isPresetStatusColor)(h),M=N||P,T=Object.assign(Object.assign({backgroundColor:h&&!M?h:void 0},null==I?void 0:I.style),p),B=O("tag",d),[H,R,W]=b(B),D=(0,n.default)(B,null==I?void 0:I.className,{[`${B}-${h}`]:M,[`${B}-has-color`]:h&&!M,[`${B}-hidden`]:!E,[`${B}-rtl`]:"rtl"===x,[`${B}-borderless`]:!y},u,g,R,W),q=e=>{e.stopPropagation(),null==$||$(e),e.defaultPrevented||j(!1)},[,G]=(0,a.useClosable)((0,a.pickClosable)(e),(0,a.pickClosable)(I),{closable:!1,closeIconRender:e=>{let r=t.createElement("span",{className:`${B}-close-icon`,onClick:q},e);return(0,l.replaceElement)(e,r,e=>({onClick:t=>{var n;null==(n=null==e?void 0:e.onClick)||n.call(e,t),q(t)},className:(0,n.default)(null==e?void 0:e.className,`${B}-close-icon`)}))}}),L="function"==typeof w.onClick||m&&"a"===m.type,A=f||null,F=A?t.createElement(t.Fragment,null,A,m&&t.createElement("span",null,m)):m,X=t.createElement("span",Object.assign({},z,{ref:s,className:D,style:T}),F,G,N&&t.createElement(v,{key:"preset",prefixCls:B}),P&&t.createElement(C,{key:"status",prefixCls:B}));return H(L?t.createElement(o.default,{component:"Tag"},X):X)});w.CheckableTag=$,e.s(["Tag",0,w],262218)},770914,e=>{"use strict";var t=e.i(38243);e.s(["Space",()=>t.default])},790848,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(739295),r=e.i(343794),i=e.i(931067),a=e.i(211577),l=e.i(392221),o=e.i(703923),c=e.i(914949),s=e.i(404948),d=["prefixCls","className","checked","defaultChecked","disabled","loadingIcon","checkedChildren","unCheckedChildren","onClick","onChange","onKeyDown"],u=t.forwardRef(function(e,n){var u,g=e.prefixCls,p=void 0===g?"rc-switch":g,m=e.className,f=e.checked,b=e.defaultChecked,h=e.disabled,$=e.loadingIcon,y=e.checkedChildren,v=e.unCheckedChildren,S=e.onClick,C=e.onChange,k=e.onKeyDown,w=(0,o.default)(e,d),O=(0,c.default)(!1,{value:f,defaultValue:b}),x=(0,l.default)(O,2),I=x[0],E=x[1];function j(e,t){var n=I;return h||(E(n=e),null==C||C(n,t)),n}var z=(0,r.default)(p,m,(u={},(0,a.default)(u,"".concat(p,"-checked"),I),(0,a.default)(u,"".concat(p,"-disabled"),h),u));return t.createElement("button",(0,i.default)({},w,{type:"button",role:"switch","aria-checked":I,disabled:h,className:z,ref:n,onKeyDown:function(e){e.which===s.default.LEFT?j(!1,e):e.which===s.default.RIGHT&&j(!0,e),null==k||k(e)},onClick:function(e){var t=j(!I,e);null==S||S(t,e)}}),$,t.createElement("span",{className:"".concat(p,"-inner")},t.createElement("span",{className:"".concat(p,"-inner-checked")},y),t.createElement("span",{className:"".concat(p,"-inner-unchecked")},v)))});u.displayName="Switch";var g=e.i(121872),p=e.i(242064),m=e.i(937328),f=e.i(517455);e.i(296059);var b=e.i(915654),h=e.i(135551),$=e.i(183293),y=e.i(246422),v=e.i(838378);let S=(0,y.genStyleHooks)("Switch",e=>{let t=(0,v.mergeToken)(e,{switchDuration:e.motionDurationMid,switchColor:e.colorPrimary,switchDisabledOpacity:e.opacityLoading,switchLoadingIconSize:e.calc(e.fontSizeIcon).mul(.75).equal(),switchLoadingIconColor:`rgba(0, 0, 0, ${e.opacityLoading})`,switchHandleActiveInset:"-30%"});return[(e=>{let{componentCls:t,trackHeight:n,trackMinWidth:r}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign({},(0,$.resetComponent)(e)),{position:"relative",display:"inline-block",boxSizing:"border-box",minWidth:r,height:n,lineHeight:(0,b.unit)(n),verticalAlign:"middle",background:e.colorTextQuaternary,border:"0",borderRadius:100,cursor:"pointer",transition:`all ${e.motionDurationMid}`,userSelect:"none",[`&:hover:not(${t}-disabled)`]:{background:e.colorTextTertiary}}),(0,$.genFocusStyle)(e)),{[`&${t}-checked`]:{background:e.switchColor,[`&:hover:not(${t}-disabled)`]:{background:e.colorPrimaryHover}},[`&${t}-loading, &${t}-disabled`]:{cursor:"not-allowed",opacity:e.switchDisabledOpacity,"*":{boxShadow:"none",cursor:"not-allowed"}},[`&${t}-rtl`]:{direction:"rtl"}})}})(t),(e=>{let{componentCls:t,trackHeight:n,trackPadding:r,innerMinMargin:i,innerMaxMargin:a,handleSize:l,calc:o}=e,c=`${t}-inner`,s=(0,b.unit)(o(l).add(o(r).mul(2)).equal()),d=(0,b.unit)(o(a).mul(2).equal());return{[t]:{[c]:{display:"block",overflow:"hidden",borderRadius:100,height:"100%",paddingInlineStart:a,paddingInlineEnd:i,transition:`padding-inline-start ${e.switchDuration} ease-in-out, padding-inline-end ${e.switchDuration} ease-in-out`,[`${c}-checked, ${c}-unchecked`]:{display:"block",color:e.colorTextLightSolid,fontSize:e.fontSizeSM,transition:`margin-inline-start ${e.switchDuration} ease-in-out, margin-inline-end ${e.switchDuration} ease-in-out`,pointerEvents:"none",minHeight:n},[`${c}-checked`]:{marginInlineStart:`calc(-100% + ${s} - ${d})`,marginInlineEnd:`calc(100% - ${s} + ${d})`},[`${c}-unchecked`]:{marginTop:o(n).mul(-1).equal(),marginInlineStart:0,marginInlineEnd:0}},[`&${t}-checked ${c}`]:{paddingInlineStart:i,paddingInlineEnd:a,[`${c}-checked`]:{marginInlineStart:0,marginInlineEnd:0},[`${c}-unchecked`]:{marginInlineStart:`calc(100% - ${s} + ${d})`,marginInlineEnd:`calc(-100% + ${s} - ${d})`}},[`&:not(${t}-disabled):active`]:{[`&:not(${t}-checked) ${c}`]:{[`${c}-unchecked`]:{marginInlineStart:o(r).mul(2).equal(),marginInlineEnd:o(r).mul(-1).mul(2).equal()}},[`&${t}-checked ${c}`]:{[`${c}-checked`]:{marginInlineStart:o(r).mul(-1).mul(2).equal(),marginInlineEnd:o(r).mul(2).equal()}}}}}})(t),(e=>{let{componentCls:t,trackPadding:n,handleBg:r,handleShadow:i,handleSize:a,calc:l}=e,o=`${t}-handle`;return{[t]:{[o]:{position:"absolute",top:n,insetInlineStart:n,width:a,height:a,transition:`all ${e.switchDuration} ease-in-out`,"&::before":{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,backgroundColor:r,borderRadius:l(a).div(2).equal(),boxShadow:i,transition:`all ${e.switchDuration} ease-in-out`,content:'""'}},[`&${t}-checked ${o}`]:{insetInlineStart:`calc(100% - ${(0,b.unit)(l(a).add(n).equal())})`},[`&:not(${t}-disabled):active`]:{[`${o}::before`]:{insetInlineEnd:e.switchHandleActiveInset,insetInlineStart:0},[`&${t}-checked ${o}::before`]:{insetInlineEnd:0,insetInlineStart:e.switchHandleActiveInset}}}}})(t),(e=>{let{componentCls:t,handleSize:n,calc:r}=e;return{[t]:{[`${t}-loading-icon${e.iconCls}`]:{position:"relative",top:r(r(n).sub(e.fontSize)).div(2).equal(),color:e.switchLoadingIconColor,verticalAlign:"top"},[`&${t}-checked ${t}-loading-icon`]:{color:e.switchColor}}}})(t),(e=>{let{componentCls:t,trackHeightSM:n,trackPadding:r,trackMinWidthSM:i,innerMinMarginSM:a,innerMaxMarginSM:l,handleSizeSM:o,calc:c}=e,s=`${t}-inner`,d=(0,b.unit)(c(o).add(c(r).mul(2)).equal()),u=(0,b.unit)(c(l).mul(2).equal());return{[t]:{[`&${t}-small`]:{minWidth:i,height:n,lineHeight:(0,b.unit)(n),[`${t}-inner`]:{paddingInlineStart:l,paddingInlineEnd:a,[`${s}-checked, ${s}-unchecked`]:{minHeight:n},[`${s}-checked`]:{marginInlineStart:`calc(-100% + ${d} - ${u})`,marginInlineEnd:`calc(100% - ${d} + ${u})`},[`${s}-unchecked`]:{marginTop:c(n).mul(-1).equal(),marginInlineStart:0,marginInlineEnd:0}},[`${t}-handle`]:{width:o,height:o},[`${t}-loading-icon`]:{top:c(c(o).sub(e.switchLoadingIconSize)).div(2).equal(),fontSize:e.switchLoadingIconSize},[`&${t}-checked`]:{[`${t}-inner`]:{paddingInlineStart:a,paddingInlineEnd:l,[`${s}-checked`]:{marginInlineStart:0,marginInlineEnd:0},[`${s}-unchecked`]:{marginInlineStart:`calc(100% - ${d} + ${u})`,marginInlineEnd:`calc(-100% + ${d} - ${u})`}},[`${t}-handle`]:{insetInlineStart:`calc(100% - ${(0,b.unit)(c(o).add(r).equal())})`}},[`&:not(${t}-disabled):active`]:{[`&:not(${t}-checked) ${s}`]:{[`${s}-unchecked`]:{marginInlineStart:c(e.marginXXS).div(2).equal(),marginInlineEnd:c(e.marginXXS).mul(-1).div(2).equal()}},[`&${t}-checked ${s}`]:{[`${s}-checked`]:{marginInlineStart:c(e.marginXXS).mul(-1).div(2).equal(),marginInlineEnd:c(e.marginXXS).div(2).equal()}}}}}}})(t)]},e=>{let{fontSize:t,lineHeight:n,controlHeight:r,colorWhite:i}=e,a=t*n,l=r/2,o=a-4,c=l-4;return{trackHeight:a,trackHeightSM:l,trackMinWidth:2*o+8,trackMinWidthSM:2*c+4,trackPadding:2,handleBg:i,handleSize:o,handleSizeSM:c,handleShadow:`0 2px 4px 0 ${new h.FastColor("#00230b").setA(.2).toRgbString()}`,innerMinMargin:o/2,innerMaxMargin:o+2+4,innerMinMarginSM:c/2,innerMaxMarginSM:c+2+4}});var C=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let k=t.forwardRef((e,i)=>{let{prefixCls:a,size:l,disabled:o,loading:s,className:d,rootClassName:b,style:h,checked:$,value:y,defaultChecked:v,defaultValue:k,onChange:w}=e,O=C(e,["prefixCls","size","disabled","loading","className","rootClassName","style","checked","value","defaultChecked","defaultValue","onChange"]),[x,I]=(0,c.default)(!1,{value:null!=$?$:y,defaultValue:null!=v?v:k}),{getPrefixCls:E,direction:j,switch:z}=t.useContext(p.ConfigContext),N=t.useContext(m.default),P=(null!=o?o:N)||s,M=E("switch",a),T=t.createElement("div",{className:`${M}-handle`},s&&t.createElement(n.default,{className:`${M}-loading-icon`})),[B,H,R]=S(M),W=(0,f.default)(l),D=(0,r.default)(null==z?void 0:z.className,{[`${M}-small`]:"small"===W,[`${M}-loading`]:s,[`${M}-rtl`]:"rtl"===j},d,b,H,R),q=Object.assign(Object.assign({},null==z?void 0:z.style),h);return B(t.createElement(g.default,{component:"Switch",disabled:P},t.createElement(u,Object.assign({},O,{checked:x,onChange:(...e)=>{I(e[0]),null==w||w.apply(void 0,e)},prefixCls:M,className:D,style:q,disabled:P,ref:i,loadingIcon:T}))))});k.__ANT_SWITCH=!0,e.s(["Switch",0,k],790848)},292639,e=>{"use strict";var t=e.i(602869),n=e.i(266027);let r=(0,e.i(243652).createQueryKeys)("uiSettings");e.s(["useUISettings",0,()=>(0,n.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,t.getUiSettings)(),staleTime:36e5,gcTime:36e5})])},492030,e=>{"use strict";var t=e.i(121229);e.s(["CheckOutlined",()=>t.default])},829672,836938,310730,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(914949),i=e.i(404948);let a=e=>e?"function"==typeof e?e():e:null;e.s(["getRenderPropValue",0,a],836938);var l=e.i(613541),o=e.i(763731),c=e.i(242064),s=e.i(491816);e.i(793154);var d=e.i(880476),u=e.i(183293),g=e.i(717356),p=e.i(320560),m=e.i(307358),f=e.i(246422),b=e.i(838378),h=e.i(617933);let $=(0,f.genStyleHooks)("Popover",e=>{let{colorBgElevated:t,colorText:n}=e,r=(0,b.mergeToken)(e,{popoverBg:t,popoverColor:n});return[(e=>{let{componentCls:t,popoverColor:n,titleMinWidth:r,fontWeightStrong:i,innerPadding:a,boxShadowSecondary:l,colorTextHeading:o,borderRadiusLG:c,zIndexPopup:s,titleMarginBottom:d,colorBgElevated:g,popoverBg:m,titleBorderBottom:f,innerContentPadding:b,titlePadding:h}=e;return[{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"absolute",top:0,left:{_skip_check_:!0,value:0},zIndex:s,fontWeight:"normal",whiteSpace:"normal",textAlign:"start",cursor:"auto",userSelect:"text","--valid-offset-x":"var(--arrow-offset-horizontal, var(--arrow-x))",transformOrigin:"var(--valid-offset-x, 50%) var(--arrow-y, 50%)","--antd-arrow-background-color":g,width:"max-content",maxWidth:"100vw","&-rtl":{direction:"rtl"},"&-hidden":{display:"none"},[`${t}-content`]:{position:"relative"},[`${t}-inner`]:{backgroundColor:m,backgroundClip:"padding-box",borderRadius:c,boxShadow:l,padding:a},[`${t}-title`]:{minWidth:r,marginBottom:d,color:o,fontWeight:i,borderBottom:f,padding:h},[`${t}-inner-content`]:{color:n,padding:b}})},(0,p.default)(e,"var(--antd-arrow-background-color)"),{[`${t}-pure`]:{position:"relative",maxWidth:"none",margin:e.sizePopupArrow,display:"inline-block",[`${t}-content`]:{display:"inline-block"}}}]})(r),(e=>{let{componentCls:t}=e;return{[t]:h.PresetColors.map(n=>{let r=e[`${n}6`];return{[`&${t}-${n}`]:{"--antd-arrow-background-color":r,[`${t}-inner`]:{backgroundColor:r},[`${t}-arrow`]:{background:"transparent"}}}})}})(r),(0,g.initZoomMotion)(r,"zoom-big")]},e=>{let{lineWidth:t,controlHeight:n,fontHeight:r,padding:i,wireframe:a,zIndexPopupBase:l,borderRadiusLG:o,marginXS:c,lineType:s,colorSplit:d,paddingSM:u}=e,g=n-r;return Object.assign(Object.assign(Object.assign({titleMinWidth:177,zIndexPopup:l+30},(0,m.getArrowToken)(e)),(0,p.getArrowOffsetToken)({contentRadius:o,limitVerticalRadius:!0})),{innerPadding:12*!a,titleMarginBottom:a?0:c,titlePadding:a?`${g/2}px ${i}px ${g/2-t}px`:0,titleBorderBottom:a?`${t}px ${s} ${d}`:"none",innerContentPadding:a?`${u}px ${i}px`:0})},{resetStyle:!1,deprecatedTokens:[["width","titleMinWidth"],["minWidth","titleMinWidth"]]});var y=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let v=({title:e,content:n,prefixCls:r})=>e||n?t.createElement(t.Fragment,null,e&&t.createElement("div",{className:`${r}-title`},e),n&&t.createElement("div",{className:`${r}-inner-content`},n)):null,S=e=>{let{hashId:r,prefixCls:i,className:l,style:o,placement:c="top",title:s,content:u,children:g}=e,p=a(s),m=a(u),f=(0,n.default)(r,i,`${i}-pure`,`${i}-placement-${c}`,l);return t.createElement("div",{className:f,style:o},t.createElement("div",{className:`${i}-arrow`}),t.createElement(d.Popup,Object.assign({},e,{className:r,prefixCls:i}),g||t.createElement(v,{prefixCls:i,title:p,content:m})))},C=e=>{let{prefixCls:r,className:i}=e,a=y(e,["prefixCls","className"]),{getPrefixCls:l}=t.useContext(c.ConfigContext),o=l("popover",r),[s,d,u]=$(o);return s(t.createElement(S,Object.assign({},a,{prefixCls:o,hashId:d,className:(0,n.default)(i,u)})))};e.s(["Overlay",0,v,"default",0,C],310730);var k=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let w=t.forwardRef((e,d)=>{var u,g;let{prefixCls:p,title:m,content:f,overlayClassName:b,placement:h="top",trigger:y="hover",children:S,mouseEnterDelay:C=.1,mouseLeaveDelay:w=.1,onOpenChange:O,overlayStyle:x={},styles:I,classNames:E}=e,j=k(e,["prefixCls","title","content","overlayClassName","placement","trigger","children","mouseEnterDelay","mouseLeaveDelay","onOpenChange","overlayStyle","styles","classNames"]),{getPrefixCls:z,className:N,style:P,classNames:M,styles:T}=(0,c.useComponentConfig)("popover"),B=z("popover",p),[H,R,W]=$(B),D=z(),q=(0,n.default)(b,R,W,N,M.root,null==E?void 0:E.root),G=(0,n.default)(M.body,null==E?void 0:E.body),[L,A]=(0,r.default)(!1,{value:null!=(u=e.open)?u:e.visible,defaultValue:null!=(g=e.defaultOpen)?g:e.defaultVisible}),F=(e,t)=>{A(e,!0),null==O||O(e,t)},X=a(m),V=a(f);return H(t.createElement(s.default,Object.assign({placement:h,trigger:y,mouseEnterDelay:C,mouseLeaveDelay:w},j,{prefixCls:B,classNames:{root:q,body:G},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},T.root),P),x),null==I?void 0:I.root),body:Object.assign(Object.assign({},T.body),null==I?void 0:I.body)},ref:d,open:L,onOpenChange:e=>{F(e)},overlay:X||V?t.createElement(v,{prefixCls:B,title:X,content:V}):null,transitionName:(0,l.getTransitionName)(D,"zoom-big",j.transitionName),"data-popover-inject":!0}),(0,o.cloneElement)(S,{onKeyDown:e=>{var n,r;(0,t.isValidElement)(S)&&(null==(r=null==S?void 0:(n=S.props).onKeyDown)||r.call(n,e)),e.keyCode===i.default.ESC&&F(!1,e)}})))});w._InternalPanelDoNotUseOrYouWillBeFired=C,e.s(["default",0,w],829672)},282786,e=>{"use strict";var t=e.i(829672);e.s(["Popover",()=>t.default])},771674,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"}}]},name:"user",theme:"outlined"};var i=e.i(9583),a=n.forwardRef(function(e,a){return n.createElement(i.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["UserOutlined",0,a],771674)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/00q4mtjboprhm.js b/litellm/proxy/_experimental/out/_next/static/chunks/02-u6qtmsnqn0.js similarity index 89% rename from litellm/proxy/_experimental/out/_next/static/chunks/00q4mtjboprhm.js rename to litellm/proxy/_experimental/out/_next/static/chunks/02-u6qtmsnqn0.js index a2355d3675e..e1a7a779038 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/00q4mtjboprhm.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02-u6qtmsnqn0.js @@ -1,4 +1,4 @@ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,152990,682830,e=>{"use strict";var t=e.i(271645);function l(e,t){return"function"==typeof e?e(t):e}function n(e,t){return n=>{t.setState(t=>({...t,[e]:l(n,t[e])}))}}function o(e){return e instanceof Function}function i(e,t,l){let n,o=[];return i=>{let r,a;l.key&&l.debug&&(r=Date.now());let u=e(i);if(!(u.length!==o.length||u.some((e,t)=>o[t]!==e)))return n;if(o=u,l.key&&l.debug&&(a=Date.now()),n=t(...u),null==l||null==l.onChange||l.onChange(n),l.key&&l.debug&&null!=l&&l.debug()){let e=Math.round((Date.now()-r)*100)/100,t=Math.round((Date.now()-a)*100)/100,n=t/16,o=(e,t)=>{for(e=String(e);e.length{var l;return null!=(l=null==e?void 0:e.debugAll)?l:e[t]},key:!1,onChange:n}}e.i(247167);let a="debugHeaders";function u(e,t,l){var n;let o={id:null!=(n=l.id)?n:t.id,column:t,index:l.index,isPlaceholder:!!l.isPlaceholder,placeholderId:l.placeholderId,depth:l.depth,subHeaders:[],colSpan:0,rowSpan:0,headerGroup:null,getLeafHeaders:()=>{let e=[],t=l=>{l.subHeaders&&l.subHeaders.length&&l.subHeaders.map(t),e.push(l)};return t(o),e},getContext:()=>({table:e,header:o,column:t})};return e._features.forEach(t=>{null==t.createHeader||t.createHeader(o,e)}),o}function g(e,t,l,n){var o,i;let r=0,a=function(e,t){void 0===t&&(t=1),r=Math.max(r,t),e.filter(e=>e.getIsVisible()).forEach(e=>{var l;null!=(l=e.columns)&&l.length&&a(e.columns,t+1)},0)};a(e);let g=[],s=(e,t)=>{let o={depth:t,id:[n,`${t}`].filter(Boolean).join("_"),headers:[]},i=[];e.forEach(e=>{let r,a=[...i].reverse()[0],g=e.column.depth===o.depth,s=!1;if(g&&e.column.parent?r=e.column.parent:(r=e.column,s=!0),a&&(null==a?void 0:a.column)===r)a.subHeaders.push(e);else{let o=u(l,r,{id:[n,t,r.id,null==e?void 0:e.id].filter(Boolean).join("_"),isPlaceholder:s,placeholderId:s?`${i.filter(e=>e.column===r).length}`:void 0,depth:t,index:i.length});o.subHeaders.push(e),i.push(o)}o.headers.push(e),e.headerGroup=o}),g.push(o),t>0&&s(i,t-1)};s(t.map((e,t)=>u(l,e,{depth:r,index:t})),r-1),g.reverse();let d=e=>e.filter(e=>e.column.getIsVisible()).map(e=>{let t=0,l=0,n=[0];return e.subHeaders&&e.subHeaders.length?(n=[],d(e.subHeaders).forEach(e=>{let{colSpan:l,rowSpan:o}=e;t+=l,n.push(o)})):t=1,l+=Math.min(...n),e.colSpan=t,e.rowSpan=l,{colSpan:t,rowSpan:l}});return d(null!=(o=null==(i=g[0])?void 0:i.headers)?o:[]),g}let s=(e,t,l,n,o,a,u)=>{let g={id:t,index:n,original:l,depth:o,parentId:u,_valuesCache:{},_uniqueValuesCache:{},getValue:t=>{if(g._valuesCache.hasOwnProperty(t))return g._valuesCache[t];let l=e.getColumn(t);if(null!=l&&l.accessorFn)return g._valuesCache[t]=l.accessorFn(g.original,n),g._valuesCache[t]},getUniqueValues:t=>{if(g._uniqueValuesCache.hasOwnProperty(t))return g._uniqueValuesCache[t];let l=e.getColumn(t);if(null!=l&&l.accessorFn)return l.columnDef.getUniqueValues?g._uniqueValuesCache[t]=l.columnDef.getUniqueValues(g.original,n):g._uniqueValuesCache[t]=[g.getValue(t)],g._uniqueValuesCache[t]},renderValue:t=>{var l;return null!=(l=g.getValue(t))?l:e.options.renderFallbackValue},subRows:null!=a?a:[],getLeafRows:()=>{var e,t;let l,n;return e=g.subRows,t=e=>e.subRows,l=[],(n=e=>{e.forEach(e=>{l.push(e);let o=t(e);null!=o&&o.length&&n(o)})})(e),l},getParentRow:()=>g.parentId?e.getRow(g.parentId,!0):void 0,getParentRows:()=>{let e=[],t=g;for(;;){let l=t.getParentRow();if(!l)break;e.push(l),t=l}return e.reverse()},getAllCells:i(()=>[e.getAllLeafColumns()],t=>t.map(t=>{var l;let n;return l=t.id,n={id:`${g.id}_${t.id}`,row:g,column:t,getValue:()=>g.getValue(l),renderValue:()=>{var t;return null!=(t=n.getValue())?t:e.options.renderFallbackValue},getContext:i(()=>[e,t,g,n],(e,t,l,n)=>({table:e,column:t,row:l,cell:n,getValue:n.getValue,renderValue:n.renderValue}),r(e.options,"debugCells","cell.getContext"))},e._features.forEach(l=>{null==l.createCell||l.createCell(n,t,g,e)},{}),n}),r(e.options,"debugRows","getAllCells")),_getAllCellsByColumnId:i(()=>[g.getAllCells()],e=>e.reduce((e,t)=>(e[t.column.id]=t,e),{}),r(e.options,"debugRows","getAllCellsByColumnId"))};for(let t=0;t{var n,o;let i=null==l||null==(n=l.toString())?void 0:n.toLowerCase();return!!(null==(o=e.getValue(t))||null==(o=o.toString())||null==(o=o.toLowerCase())?void 0:o.includes(i))};d.autoRemove=e=>h(e);let p=(e,t,l)=>{var n;return!!(null==(n=e.getValue(t))||null==(n=n.toString())?void 0:n.includes(l))};p.autoRemove=e=>h(e);let c=(e,t,l)=>{var n;return(null==(n=e.getValue(t))||null==(n=n.toString())?void 0:n.toLowerCase())===(null==l?void 0:l.toLowerCase())};c.autoRemove=e=>h(e);let f=(e,t,l)=>{var n;return null==(n=e.getValue(t))?void 0:n.includes(l)};f.autoRemove=e=>h(e);let m=(e,t,l)=>!l.some(l=>{var n;return!(null!=(n=e.getValue(t))&&n.includes(l))});m.autoRemove=e=>h(e)||!(null!=e&&e.length);let C=(e,t,l)=>l.some(l=>{var n;return null==(n=e.getValue(t))?void 0:n.includes(l)});C.autoRemove=e=>h(e)||!(null!=e&&e.length);let w=(e,t,l)=>e.getValue(t)===l;w.autoRemove=e=>h(e);let S=(e,t,l)=>e.getValue(t)==l;S.autoRemove=e=>h(e);let R=(e,t,l)=>{let[n,o]=l,i=e.getValue(t);return i>=n&&i<=o};R.resolveFilterValue=e=>{let[t,l]=e,n="number"!=typeof t?parseFloat(t):t,o="number"!=typeof l?parseFloat(l):l,i=null===t||Number.isNaN(n)?-1/0:n,r=null===l||Number.isNaN(o)?1/0:o;if(i>r){let e=i;i=r,r=e}return[i,r]},R.autoRemove=e=>h(e)||h(e[0])&&h(e[1]);let v={includesString:d,includesStringSensitive:p,equalsString:c,arrIncludes:f,arrIncludesAll:m,arrIncludesSome:C,equals:w,weakEquals:S,inNumberRange:R};function h(e){return null==e||""===e}function b(e,t,l){return!!e&&!!e.autoRemove&&e.autoRemove(t,l)||void 0===t||"string"==typeof t&&!t}let F={sum:(e,t,l)=>l.reduce((t,l)=>{let n=l.getValue(e);return t+("number"==typeof n?n:0)},0),min:(e,t,l)=>{let n;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(n>l||void 0===n&&l>=l)&&(n=l)}),n},max:(e,t,l)=>{let n;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(n=l)&&(n=l)}),n},extent:(e,t,l)=>{let n,o;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(void 0===n?l>=l&&(n=o=l):(n>l&&(n=l),o{let l=0,n=0;if(t.forEach(t=>{let o=t.getValue(e);null!=o&&(o*=1)>=o&&(++l,n+=o)}),l)return n/l},median:(e,t)=>{if(!t.length)return;let l=t.map(t=>t.getValue(e));if(!(Array.isArray(l)&&l.every(e=>"number"==typeof e)))return;if(1===l.length)return l[0];let n=Math.floor(l.length/2),o=l.sort((e,t)=>e-t);return l.length%2!=0?o[n]:(o[n-1]+o[n])/2},unique:(e,t)=>Array.from(new Set(t.map(t=>t.getValue(e))).values()),uniqueCount:(e,t)=>new Set(t.map(t=>t.getValue(e))).size,count:(e,t)=>t.length},M=()=>({left:[],right:[]}),V={size:150,minSize:20,maxSize:Number.MAX_SAFE_INTEGER},P=()=>({startOffset:null,startSize:null,deltaOffset:null,deltaPercentage:null,isResizingColumn:!1,columnSizingStart:[]}),I=null;function x(e){return"touchstart"===e.type}function _(e,t){return t?"center"===t?e.getCenterVisibleLeafColumns():"left"===t?e.getLeftVisibleLeafColumns():e.getRightVisibleLeafColumns():e.getVisibleLeafColumns()}let y=()=>({pageIndex:0,pageSize:10}),E=()=>({top:[],bottom:[]}),G=(e,t,l,n,o)=>{var i;let r=o.getRow(t,!0);l?(r.getCanMultiSelect()||Object.keys(e).forEach(t=>delete e[t]),r.getCanSelect()&&(e[t]=!0)):delete e[t],n&&null!=(i=r.subRows)&&i.length&&r.getCanSelectSubRows()&&r.subRows.forEach(t=>G(e,t.id,l,n,o))};function L(e,t){let l=e.getState().rowSelection,n=[],o={},i=function(e,t){return e.map(e=>{var t;let r=A(e,l);if(r&&(n.push(e),o[e.id]=e),null!=(t=e.subRows)&&t.length&&(e={...e,subRows:i(e.subRows)}),r)return e}).filter(Boolean)};return{rows:i(t.rows),flatRows:n,rowsById:o}}function A(e,t){var l;return null!=(l=t[e.id])&&l}function H(e,t,l){var n;if(!(null!=(n=e.subRows)&&n.length))return!1;let o=!0,i=!1;return e.subRows.forEach(e=>{if((!i||o)&&(e.getCanSelect()&&(A(e,t)?i=!0:o=!1),e.subRows&&e.subRows.length)){let l=H(e,t);"all"===l?i=!0:("some"===l&&(i=!0),o=!1)}}),o?"all":!!i&&"some"}let D=/([0-9]+)/gm;function z(e,t){return e===t?0:e>t?1:-1}function O(e){return"number"==typeof e?isNaN(e)||e===1/0||e===-1/0?"":String(e):"string"==typeof e?e:""}function T(e,t){let l=e.split(D).filter(Boolean),n=t.split(D).filter(Boolean);for(;l.length&&n.length;){let e=l.shift(),t=n.shift(),o=parseInt(e,10),i=parseInt(t,10),r=[o,i].sort();if(isNaN(r[0])){if(e>t)return 1;if(t>e)return -1;continue}if(isNaN(r[1]))return isNaN(o)?-1:1;if(o>i)return 1;if(i>o)return -1}return l.length-n.length}let B={alphanumeric:(e,t,l)=>T(O(e.getValue(l)).toLowerCase(),O(t.getValue(l)).toLowerCase()),alphanumericCaseSensitive:(e,t,l)=>T(O(e.getValue(l)),O(t.getValue(l))),text:(e,t,l)=>z(O(e.getValue(l)).toLowerCase(),O(t.getValue(l)).toLowerCase()),textCaseSensitive:(e,t,l)=>z(O(e.getValue(l)),O(t.getValue(l))),datetime:(e,t,l)=>{let n=e.getValue(l),o=t.getValue(l);return n>o?1:nz(e.getValue(l),t.getValue(l))},q=[{createTable:e=>{e.getHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(t,l,n,o)=>{var i,r;let a=null!=(i=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?i:[],u=null!=(r=null==o?void 0:o.map(e=>l.find(t=>t.id===e)).filter(Boolean))?r:[];return g(t,[...a,...l.filter(e=>!(null!=n&&n.includes(e.id))&&!(null!=o&&o.includes(e.id))),...u],e)},r(e.options,a,"getHeaderGroups")),e.getCenterHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(t,l,n,o)=>g(t,l=l.filter(e=>!(null!=n&&n.includes(e.id))&&!(null!=o&&o.includes(e.id))),e,"center"),r(e.options,a,"getCenterHeaderGroups")),e.getLeftHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left],(t,l,n)=>{var o;return g(t,null!=(o=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?o:[],e,"left")},r(e.options,a,"getLeftHeaderGroups")),e.getRightHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.right],(t,l,n)=>{var o;return g(t,null!=(o=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?o:[],e,"right")},r(e.options,a,"getRightHeaderGroups")),e.getFooterGroups=i(()=>[e.getHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getFooterGroups")),e.getLeftFooterGroups=i(()=>[e.getLeftHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getLeftFooterGroups")),e.getCenterFooterGroups=i(()=>[e.getCenterHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getCenterFooterGroups")),e.getRightFooterGroups=i(()=>[e.getRightHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getRightFooterGroups")),e.getFlatHeaders=i(()=>[e.getHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getFlatHeaders")),e.getLeftFlatHeaders=i(()=>[e.getLeftHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getLeftFlatHeaders")),e.getCenterFlatHeaders=i(()=>[e.getCenterHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getCenterFlatHeaders")),e.getRightFlatHeaders=i(()=>[e.getRightHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getRightFlatHeaders")),e.getCenterLeafHeaders=i(()=>[e.getCenterFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getCenterLeafHeaders")),e.getLeftLeafHeaders=i(()=>[e.getLeftFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getLeftLeafHeaders")),e.getRightLeafHeaders=i(()=>[e.getRightFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getRightLeafHeaders")),e.getLeafHeaders=i(()=>[e.getLeftHeaderGroups(),e.getCenterHeaderGroups(),e.getRightHeaderGroups()],(e,t,l)=>{var n,o,i,r,a,u;return[...null!=(n=null==(o=e[0])?void 0:o.headers)?n:[],...null!=(i=null==(r=t[0])?void 0:r.headers)?i:[],...null!=(a=null==(u=l[0])?void 0:u.headers)?a:[]].map(e=>e.getLeafHeaders()).flat()},r(e.options,a,"getLeafHeaders"))}},{getInitialState:e=>({columnVisibility:{},...e}),getDefaultOptions:e=>({onColumnVisibilityChange:n("columnVisibility",e)}),createColumn:(e,t)=>{e.toggleVisibility=l=>{e.getCanHide()&&t.setColumnVisibility(t=>({...t,[e.id]:null!=l?l:!e.getIsVisible()}))},e.getIsVisible=()=>{var l,n;let o=e.columns;return null==(l=o.length?o.some(e=>e.getIsVisible()):null==(n=t.getState().columnVisibility)?void 0:n[e.id])||l},e.getCanHide=()=>{var l,n;return(null==(l=e.columnDef.enableHiding)||l)&&(null==(n=t.options.enableHiding)||n)},e.getToggleVisibilityHandler=()=>t=>{null==e.toggleVisibility||e.toggleVisibility(t.target.checked)}},createRow:(e,t)=>{e._getAllVisibleCells=i(()=>[e.getAllCells(),t.getState().columnVisibility],e=>e.filter(e=>e.column.getIsVisible()),r(t.options,"debugRows","_getAllVisibleCells")),e.getVisibleCells=i(()=>[e.getLeftVisibleCells(),e.getCenterVisibleCells(),e.getRightVisibleCells()],(e,t,l)=>[...e,...t,...l],r(t.options,"debugRows","getVisibleCells"))},createTable:e=>{let t=(t,l)=>i(()=>[l(),l().filter(e=>e.getIsVisible()).map(e=>e.id).join("_")],e=>e.filter(e=>null==e.getIsVisible?void 0:e.getIsVisible()),r(e.options,"debugColumns",t));e.getVisibleFlatColumns=t("getVisibleFlatColumns",()=>e.getAllFlatColumns()),e.getVisibleLeafColumns=t("getVisibleLeafColumns",()=>e.getAllLeafColumns()),e.getLeftVisibleLeafColumns=t("getLeftVisibleLeafColumns",()=>e.getLeftLeafColumns()),e.getRightVisibleLeafColumns=t("getRightVisibleLeafColumns",()=>e.getRightLeafColumns()),e.getCenterVisibleLeafColumns=t("getCenterVisibleLeafColumns",()=>e.getCenterLeafColumns()),e.setColumnVisibility=t=>null==e.options.onColumnVisibilityChange?void 0:e.options.onColumnVisibilityChange(t),e.resetColumnVisibility=t=>{var l;e.setColumnVisibility(t?{}:null!=(l=e.initialState.columnVisibility)?l:{})},e.toggleAllColumnsVisible=t=>{var l;t=null!=(l=t)?l:!e.getIsAllColumnsVisible(),e.setColumnVisibility(e.getAllLeafColumns().reduce((e,l)=>({...e,[l.id]:t||!(null!=l.getCanHide&&l.getCanHide())}),{}))},e.getIsAllColumnsVisible=()=>!e.getAllLeafColumns().some(e=>!(null!=e.getIsVisible&&e.getIsVisible())),e.getIsSomeColumnsVisible=()=>e.getAllLeafColumns().some(e=>null==e.getIsVisible?void 0:e.getIsVisible()),e.getToggleAllColumnsVisibilityHandler=()=>t=>{var l;e.toggleAllColumnsVisible(null==(l=t.target)?void 0:l.checked)}}},{getInitialState:e=>({columnOrder:[],...e}),getDefaultOptions:e=>({onColumnOrderChange:n("columnOrder",e)}),createColumn:(e,t)=>{e.getIndex=i(e=>[_(t,e)],t=>t.findIndex(t=>t.id===e.id),r(t.options,"debugColumns","getIndex")),e.getIsFirstColumn=l=>{var n;return(null==(n=_(t,l)[0])?void 0:n.id)===e.id},e.getIsLastColumn=l=>{var n;let o=_(t,l);return(null==(n=o[o.length-1])?void 0:n.id)===e.id}},createTable:e=>{e.setColumnOrder=t=>null==e.options.onColumnOrderChange?void 0:e.options.onColumnOrderChange(t),e.resetColumnOrder=t=>{var l;e.setColumnOrder(t?[]:null!=(l=e.initialState.columnOrder)?l:[])},e._getOrderColumnsFn=i(()=>[e.getState().columnOrder,e.getState().grouping,e.options.groupedColumnMode],(e,t,l)=>n=>{let o=[];if(null!=e&&e.length){let t=[...e],l=[...n];for(;l.length&&t.length;){let e=t.shift(),n=l.findIndex(t=>t.id===e);n>-1&&o.push(l.splice(n,1)[0])}o=[...o,...l]}else o=n;var i=o;if(!(null!=t&&t.length)||!l)return i;let r=i.filter(e=>!t.includes(e.id));return"remove"===l?r:[...t.map(e=>i.find(t=>t.id===e)).filter(Boolean),...r]},r(e.options,"debugTable","_getOrderColumnsFn"))}},{getInitialState:e=>({columnPinning:M(),...e}),getDefaultOptions:e=>({onColumnPinningChange:n("columnPinning",e)}),createColumn:(e,t)=>{e.pin=l=>{let n=e.getLeafColumns().map(e=>e.id).filter(Boolean);t.setColumnPinning(e=>{var t,o,i,r,a,u;return"right"===l?{left:(null!=(i=null==e?void 0:e.left)?i:[]).filter(e=>!(null!=n&&n.includes(e))),right:[...(null!=(r=null==e?void 0:e.right)?r:[]).filter(e=>!(null!=n&&n.includes(e))),...n]}:"left"===l?{left:[...(null!=(a=null==e?void 0:e.left)?a:[]).filter(e=>!(null!=n&&n.includes(e))),...n],right:(null!=(u=null==e?void 0:e.right)?u:[]).filter(e=>!(null!=n&&n.includes(e)))}:{left:(null!=(t=null==e?void 0:e.left)?t:[]).filter(e=>!(null!=n&&n.includes(e))),right:(null!=(o=null==e?void 0:e.right)?o:[]).filter(e=>!(null!=n&&n.includes(e)))}})},e.getCanPin=()=>e.getLeafColumns().some(e=>{var l,n,o;return(null==(l=e.columnDef.enablePinning)||l)&&(null==(n=null!=(o=t.options.enableColumnPinning)?o:t.options.enablePinning)||n)}),e.getIsPinned=()=>{let l=e.getLeafColumns().map(e=>e.id),{left:n,right:o}=t.getState().columnPinning,i=l.some(e=>null==n?void 0:n.includes(e)),r=l.some(e=>null==o?void 0:o.includes(e));return i?"left":!!r&&"right"},e.getPinnedIndex=()=>{var l,n;let o=e.getIsPinned();return o?null!=(l=null==(n=t.getState().columnPinning)||null==(n=n[o])?void 0:n.indexOf(e.id))?l:-1:0}},createRow:(e,t)=>{e.getCenterVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.left,t.getState().columnPinning.right],(e,t,l)=>{let n=[...null!=t?t:[],...null!=l?l:[]];return e.filter(e=>!n.includes(e.column.id))},r(t.options,"debugRows","getCenterVisibleCells")),e.getLeftVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.left],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.column.id===t)).filter(Boolean).map(e=>({...e,position:"left"})),r(t.options,"debugRows","getLeftVisibleCells")),e.getRightVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.right],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.column.id===t)).filter(Boolean).map(e=>({...e,position:"right"})),r(t.options,"debugRows","getRightVisibleCells"))},createTable:e=>{e.setColumnPinning=t=>null==e.options.onColumnPinningChange?void 0:e.options.onColumnPinningChange(t),e.resetColumnPinning=t=>{var l,n;return e.setColumnPinning(t?M():null!=(l=null==(n=e.initialState)?void 0:n.columnPinning)?l:M())},e.getIsSomeColumnsPinned=t=>{var l,n,o;let i=e.getState().columnPinning;return t?!!(null==(l=i[t])?void 0:l.length):!!((null==(n=i.left)?void 0:n.length)||(null==(o=i.right)?void 0:o.length))},e.getLeftLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.left],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.id===t)).filter(Boolean),r(e.options,"debugColumns","getLeftLeafColumns")),e.getRightLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.right],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.id===t)).filter(Boolean),r(e.options,"debugColumns","getRightLeafColumns")),e.getCenterLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(e,t,l)=>{let n=[...null!=t?t:[],...null!=l?l:[]];return e.filter(e=>!n.includes(e.id))},r(e.options,"debugColumns","getCenterLeafColumns"))}},{createColumn:(e,t)=>{e._getFacetedRowModel=t.options.getFacetedRowModel&&t.options.getFacetedRowModel(t,e.id),e.getFacetedRowModel=()=>e._getFacetedRowModel?e._getFacetedRowModel():t.getPreFilteredRowModel(),e._getFacetedUniqueValues=t.options.getFacetedUniqueValues&&t.options.getFacetedUniqueValues(t,e.id),e.getFacetedUniqueValues=()=>e._getFacetedUniqueValues?e._getFacetedUniqueValues():new Map,e._getFacetedMinMaxValues=t.options.getFacetedMinMaxValues&&t.options.getFacetedMinMaxValues(t,e.id),e.getFacetedMinMaxValues=()=>{if(e._getFacetedMinMaxValues)return e._getFacetedMinMaxValues()}}},{getDefaultColumnDef:()=>({filterFn:"auto"}),getInitialState:e=>({columnFilters:[],...e}),getDefaultOptions:e=>({onColumnFiltersChange:n("columnFilters",e),filterFromLeafRows:!1,maxLeafRowFilterDepth:100}),createColumn:(e,t)=>{e.getAutoFilterFn=()=>{let l=t.getCoreRowModel().flatRows[0],n=null==l?void 0:l.getValue(e.id);return"string"==typeof n?v.includesString:"number"==typeof n?v.inNumberRange:"boolean"==typeof n||null!==n&&"object"==typeof n?v.equals:Array.isArray(n)?v.arrIncludes:v.weakEquals},e.getFilterFn=()=>{var l,n;return o(e.columnDef.filterFn)?e.columnDef.filterFn:"auto"===e.columnDef.filterFn?e.getAutoFilterFn():null!=(l=null==(n=t.options.filterFns)?void 0:n[e.columnDef.filterFn])?l:v[e.columnDef.filterFn]},e.getCanFilter=()=>{var l,n,o;return(null==(l=e.columnDef.enableColumnFilter)||l)&&(null==(n=t.options.enableColumnFilters)||n)&&(null==(o=t.options.enableFilters)||o)&&!!e.accessorFn},e.getIsFiltered=()=>e.getFilterIndex()>-1,e.getFilterValue=()=>{var l;return null==(l=t.getState().columnFilters)||null==(l=l.find(t=>t.id===e.id))?void 0:l.value},e.getFilterIndex=()=>{var l,n;return null!=(l=null==(n=t.getState().columnFilters)?void 0:n.findIndex(t=>t.id===e.id))?l:-1},e.setFilterValue=n=>{t.setColumnFilters(t=>{var o,i;let r=e.getFilterFn(),a=null==t?void 0:t.find(t=>t.id===e.id),u=l(n,a?a.value:void 0);if(b(r,u,e))return null!=(o=null==t?void 0:t.filter(t=>t.id!==e.id))?o:[];let g={id:e.id,value:u};return a?null!=(i=null==t?void 0:t.map(t=>t.id===e.id?g:t))?i:[]:null!=t&&t.length?[...t,g]:[g]})}},createRow:(e,t)=>{e.columnFilters={},e.columnFiltersMeta={}},createTable:e=>{e.setColumnFilters=t=>{let n=e.getAllLeafColumns();null==e.options.onColumnFiltersChange||e.options.onColumnFiltersChange(e=>{var o;return null==(o=l(t,e))?void 0:o.filter(e=>{let t=n.find(t=>t.id===e.id);return!(t&&b(t.getFilterFn(),e.value,t))&&!0})})},e.resetColumnFilters=t=>{var l,n;e.setColumnFilters(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.columnFilters)?l:[])},e.getPreFilteredRowModel=()=>e.getCoreRowModel(),e.getFilteredRowModel=()=>(!e._getFilteredRowModel&&e.options.getFilteredRowModel&&(e._getFilteredRowModel=e.options.getFilteredRowModel(e)),e.options.manualFiltering||!e._getFilteredRowModel)?e.getPreFilteredRowModel():e._getFilteredRowModel()}},{createTable:e=>{e._getGlobalFacetedRowModel=e.options.getFacetedRowModel&&e.options.getFacetedRowModel(e,"__global__"),e.getGlobalFacetedRowModel=()=>e.options.manualFiltering||!e._getGlobalFacetedRowModel?e.getPreFilteredRowModel():e._getGlobalFacetedRowModel(),e._getGlobalFacetedUniqueValues=e.options.getFacetedUniqueValues&&e.options.getFacetedUniqueValues(e,"__global__"),e.getGlobalFacetedUniqueValues=()=>e._getGlobalFacetedUniqueValues?e._getGlobalFacetedUniqueValues():new Map,e._getGlobalFacetedMinMaxValues=e.options.getFacetedMinMaxValues&&e.options.getFacetedMinMaxValues(e,"__global__"),e.getGlobalFacetedMinMaxValues=()=>{if(e._getGlobalFacetedMinMaxValues)return e._getGlobalFacetedMinMaxValues()}}},{getInitialState:e=>({globalFilter:void 0,...e}),getDefaultOptions:e=>({onGlobalFilterChange:n("globalFilter",e),globalFilterFn:"auto",getColumnCanGlobalFilter:t=>{var l;let n=null==(l=e.getCoreRowModel().flatRows[0])||null==(l=l._getAllCellsByColumnId()[t.id])?void 0:l.getValue();return"string"==typeof n||"number"==typeof n}}),createColumn:(e,t)=>{e.getCanGlobalFilter=()=>{var l,n,o,i;return(null==(l=e.columnDef.enableGlobalFilter)||l)&&(null==(n=t.options.enableGlobalFilter)||n)&&(null==(o=t.options.enableFilters)||o)&&(null==(i=null==t.options.getColumnCanGlobalFilter?void 0:t.options.getColumnCanGlobalFilter(e))||i)&&!!e.accessorFn}},createTable:e=>{e.getGlobalAutoFilterFn=()=>v.includesString,e.getGlobalFilterFn=()=>{var t,l;let{globalFilterFn:n}=e.options;return o(n)?n:"auto"===n?e.getGlobalAutoFilterFn():null!=(t=null==(l=e.options.filterFns)?void 0:l[n])?t:v[n]},e.setGlobalFilter=t=>{null==e.options.onGlobalFilterChange||e.options.onGlobalFilterChange(t)},e.resetGlobalFilter=t=>{e.setGlobalFilter(t?void 0:e.initialState.globalFilter)}}},{getInitialState:e=>({sorting:[],...e}),getDefaultColumnDef:()=>({sortingFn:"auto",sortUndefined:1}),getDefaultOptions:e=>({onSortingChange:n("sorting",e),isMultiSortEvent:e=>e.shiftKey}),createColumn:(e,t)=>{e.getAutoSortingFn=()=>{let l=t.getFilteredRowModel().flatRows.slice(10),n=!1;for(let t of l){let l=null==t?void 0:t.getValue(e.id);if("[object Date]"===Object.prototype.toString.call(l))return B.datetime;if("string"==typeof l&&(n=!0,l.split(D).length>1))return B.alphanumeric}return n?B.text:B.basic},e.getAutoSortDir=()=>{let l=t.getFilteredRowModel().flatRows[0];return"string"==typeof(null==l?void 0:l.getValue(e.id))?"asc":"desc"},e.getSortingFn=()=>{var l,n;if(!e)throw Error();return o(e.columnDef.sortingFn)?e.columnDef.sortingFn:"auto"===e.columnDef.sortingFn?e.getAutoSortingFn():null!=(l=null==(n=t.options.sortingFns)?void 0:n[e.columnDef.sortingFn])?l:B[e.columnDef.sortingFn]},e.toggleSorting=(l,n)=>{let o=e.getNextSortingOrder(),i=null!=l;t.setSorting(r=>{let a,u=null==r?void 0:r.find(t=>t.id===e.id),g=null==r?void 0:r.findIndex(t=>t.id===e.id),s=[],d=i?l:"desc"===o;if("toggle"!=(a=null!=r&&r.length&&e.getCanMultiSort()&&n?u?"toggle":"add":null!=r&&r.length&&g!==r.length-1?"replace":u?"toggle":"replace")||i||o||(a="remove"),"add"===a){var p;(s=[...r,{id:e.id,desc:d}]).splice(0,s.length-(null!=(p=t.options.maxMultiSortColCount)?p:Number.MAX_SAFE_INTEGER))}else s="toggle"===a?r.map(t=>t.id===e.id?{...t,desc:d}:t):"remove"===a?r.filter(t=>t.id!==e.id):[{id:e.id,desc:d}];return s})},e.getFirstSortDir=()=>{var l,n;return(null!=(l=null!=(n=e.columnDef.sortDescFirst)?n:t.options.sortDescFirst)?l:"desc"===e.getAutoSortDir())?"desc":"asc"},e.getNextSortingOrder=l=>{var n,o;let i=e.getFirstSortDir(),r=e.getIsSorted();return r?(r===i||null!=(n=t.options.enableSortingRemoval)&&!n||!!l&&null!=(o=t.options.enableMultiRemove)&&!o)&&("desc"===r?"asc":"desc"):i},e.getCanSort=()=>{var l,n;return(null==(l=e.columnDef.enableSorting)||l)&&(null==(n=t.options.enableSorting)||n)&&!!e.accessorFn},e.getCanMultiSort=()=>{var l,n;return null!=(l=null!=(n=e.columnDef.enableMultiSort)?n:t.options.enableMultiSort)?l:!!e.accessorFn},e.getIsSorted=()=>{var l;let n=null==(l=t.getState().sorting)?void 0:l.find(t=>t.id===e.id);return!!n&&(n.desc?"desc":"asc")},e.getSortIndex=()=>{var l,n;return null!=(l=null==(n=t.getState().sorting)?void 0:n.findIndex(t=>t.id===e.id))?l:-1},e.clearSorting=()=>{t.setSorting(t=>null!=t&&t.length?t.filter(t=>t.id!==e.id):[])},e.getToggleSortingHandler=()=>{let l=e.getCanSort();return n=>{l&&(null==n.persist||n.persist(),null==e.toggleSorting||e.toggleSorting(void 0,!!e.getCanMultiSort()&&(null==t.options.isMultiSortEvent?void 0:t.options.isMultiSortEvent(n))))}}},createTable:e=>{e.setSorting=t=>null==e.options.onSortingChange?void 0:e.options.onSortingChange(t),e.resetSorting=t=>{var l,n;e.setSorting(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.sorting)?l:[])},e.getPreSortedRowModel=()=>e.getGroupedRowModel(),e.getSortedRowModel=()=>(!e._getSortedRowModel&&e.options.getSortedRowModel&&(e._getSortedRowModel=e.options.getSortedRowModel(e)),e.options.manualSorting||!e._getSortedRowModel)?e.getPreSortedRowModel():e._getSortedRowModel()}},{getDefaultColumnDef:()=>({aggregatedCell:e=>{var t,l;return null!=(t=null==(l=e.getValue())||null==l.toString?void 0:l.toString())?t:null},aggregationFn:"auto"}),getInitialState:e=>({grouping:[],...e}),getDefaultOptions:e=>({onGroupingChange:n("grouping",e),groupedColumnMode:"reorder"}),createColumn:(e,t)=>{e.toggleGrouping=()=>{t.setGrouping(t=>null!=t&&t.includes(e.id)?t.filter(t=>t!==e.id):[...null!=t?t:[],e.id])},e.getCanGroup=()=>{var l,n;return(null==(l=e.columnDef.enableGrouping)||l)&&(null==(n=t.options.enableGrouping)||n)&&(!!e.accessorFn||!!e.columnDef.getGroupingValue)},e.getIsGrouped=()=>{var l;return null==(l=t.getState().grouping)?void 0:l.includes(e.id)},e.getGroupedIndex=()=>{var l;return null==(l=t.getState().grouping)?void 0:l.indexOf(e.id)},e.getToggleGroupingHandler=()=>{let t=e.getCanGroup();return()=>{t&&e.toggleGrouping()}},e.getAutoAggregationFn=()=>{let l=t.getCoreRowModel().flatRows[0],n=null==l?void 0:l.getValue(e.id);return"number"==typeof n?F.sum:"[object Date]"===Object.prototype.toString.call(n)?F.extent:void 0},e.getAggregationFn=()=>{var l,n;if(!e)throw Error();return o(e.columnDef.aggregationFn)?e.columnDef.aggregationFn:"auto"===e.columnDef.aggregationFn?e.getAutoAggregationFn():null!=(l=null==(n=t.options.aggregationFns)?void 0:n[e.columnDef.aggregationFn])?l:F[e.columnDef.aggregationFn]}},createTable:e=>{e.setGrouping=t=>null==e.options.onGroupingChange?void 0:e.options.onGroupingChange(t),e.resetGrouping=t=>{var l,n;e.setGrouping(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.grouping)?l:[])},e.getPreGroupedRowModel=()=>e.getFilteredRowModel(),e.getGroupedRowModel=()=>(!e._getGroupedRowModel&&e.options.getGroupedRowModel&&(e._getGroupedRowModel=e.options.getGroupedRowModel(e)),e.options.manualGrouping||!e._getGroupedRowModel)?e.getPreGroupedRowModel():e._getGroupedRowModel()},createRow:(e,t)=>{e.getIsGrouped=()=>!!e.groupingColumnId,e.getGroupingValue=l=>{if(e._groupingValuesCache.hasOwnProperty(l))return e._groupingValuesCache[l];let n=t.getColumn(l);return null!=n&&n.columnDef.getGroupingValue?(e._groupingValuesCache[l]=n.columnDef.getGroupingValue(e.original),e._groupingValuesCache[l]):e.getValue(l)},e._groupingValuesCache={}},createCell:(e,t,l,n)=>{e.getIsGrouped=()=>t.getIsGrouped()&&t.id===l.groupingColumnId,e.getIsPlaceholder=()=>!e.getIsGrouped()&&t.getIsGrouped(),e.getIsAggregated=()=>{var t;return!e.getIsGrouped()&&!e.getIsPlaceholder()&&!!(null!=(t=l.subRows)&&t.length)}}},{getInitialState:e=>({expanded:{},...e}),getDefaultOptions:e=>({onExpandedChange:n("expanded",e),paginateExpandedRows:!0}),createTable:e=>{let t=!1,l=!1;e._autoResetExpanded=()=>{var n,o;if(!t)return void e._queue(()=>{t=!0});if(null!=(n=null!=(o=e.options.autoResetAll)?o:e.options.autoResetExpanded)?n:!e.options.manualExpanding){if(l)return;l=!0,e._queue(()=>{e.resetExpanded(),l=!1})}},e.setExpanded=t=>null==e.options.onExpandedChange?void 0:e.options.onExpandedChange(t),e.toggleAllRowsExpanded=t=>{(null!=t?t:!e.getIsAllRowsExpanded())?e.setExpanded(!0):e.setExpanded({})},e.resetExpanded=t=>{var l,n;e.setExpanded(t?{}:null!=(l=null==(n=e.initialState)?void 0:n.expanded)?l:{})},e.getCanSomeRowsExpand=()=>e.getPrePaginationRowModel().flatRows.some(e=>e.getCanExpand()),e.getToggleAllRowsExpandedHandler=()=>t=>{null==t.persist||t.persist(),e.toggleAllRowsExpanded()},e.getIsSomeRowsExpanded=()=>{let t=e.getState().expanded;return!0===t||Object.values(t).some(Boolean)},e.getIsAllRowsExpanded=()=>{let t=e.getState().expanded;return"boolean"==typeof t?!0===t:!(!Object.keys(t).length||e.getRowModel().flatRows.some(e=>!e.getIsExpanded()))},e.getExpandedDepth=()=>{let t=0;return(!0===e.getState().expanded?Object.keys(e.getRowModel().rowsById):Object.keys(e.getState().expanded)).forEach(e=>{let l=e.split(".");t=Math.max(t,l.length)}),t},e.getPreExpandedRowModel=()=>e.getSortedRowModel(),e.getExpandedRowModel=()=>(!e._getExpandedRowModel&&e.options.getExpandedRowModel&&(e._getExpandedRowModel=e.options.getExpandedRowModel(e)),e.options.manualExpanding||!e._getExpandedRowModel)?e.getPreExpandedRowModel():e._getExpandedRowModel()},createRow:(e,t)=>{e.toggleExpanded=l=>{t.setExpanded(n=>{var o;let i=!0===n||!!(null!=n&&n[e.id]),r={};if(!0===n?Object.keys(t.getRowModel().rowsById).forEach(e=>{r[e]=!0}):r=n,l=null!=(o=l)?o:!i,!i&&l)return{...r,[e.id]:!0};if(i&&!l){let{[e.id]:t,...l}=r;return l}return n})},e.getIsExpanded=()=>{var l;let n=t.getState().expanded;return!!(null!=(l=null==t.options.getIsRowExpanded?void 0:t.options.getIsRowExpanded(e))?l:!0===n||(null==n?void 0:n[e.id]))},e.getCanExpand=()=>{var l,n,o;return null!=(l=null==t.options.getRowCanExpand?void 0:t.options.getRowCanExpand(e))?l:(null==(n=t.options.enableExpanding)||n)&&!!(null!=(o=e.subRows)&&o.length)},e.getIsAllParentsExpanded=()=>{let l=!0,n=e;for(;l&&n.parentId;)l=(n=t.getRow(n.parentId,!0)).getIsExpanded();return l},e.getToggleExpandedHandler=()=>{let t=e.getCanExpand();return()=>{t&&e.toggleExpanded()}}}},{getInitialState:e=>({...e,pagination:{...y(),...null==e?void 0:e.pagination}}),getDefaultOptions:e=>({onPaginationChange:n("pagination",e)}),createTable:e=>{let t=!1,n=!1;e._autoResetPageIndex=()=>{var l,o;if(!t)return void e._queue(()=>{t=!0});if(null!=(l=null!=(o=e.options.autoResetAll)?o:e.options.autoResetPageIndex)?l:!e.options.manualPagination){if(n)return;n=!0,e._queue(()=>{e.resetPageIndex(),n=!1})}},e.setPagination=t=>null==e.options.onPaginationChange?void 0:e.options.onPaginationChange(e=>l(t,e)),e.resetPagination=t=>{var l;e.setPagination(t?y():null!=(l=e.initialState.pagination)?l:y())},e.setPageIndex=t=>{e.setPagination(n=>{let o=l(t,n.pageIndex);return o=Math.max(0,Math.min(o,void 0===e.options.pageCount||-1===e.options.pageCount?Number.MAX_SAFE_INTEGER:e.options.pageCount-1)),{...n,pageIndex:o}})},e.resetPageIndex=t=>{var l,n;e.setPageIndex(t?0:null!=(l=null==(n=e.initialState)||null==(n=n.pagination)?void 0:n.pageIndex)?l:0)},e.resetPageSize=t=>{var l,n;e.setPageSize(t?10:null!=(l=null==(n=e.initialState)||null==(n=n.pagination)?void 0:n.pageSize)?l:10)},e.setPageSize=t=>{e.setPagination(e=>{let n=Math.max(1,l(t,e.pageSize)),o=Math.floor(e.pageSize*e.pageIndex/n);return{...e,pageIndex:o,pageSize:n}})},e.setPageCount=t=>e.setPagination(n=>{var o;let i=l(t,null!=(o=e.options.pageCount)?o:-1);return"number"==typeof i&&(i=Math.max(-1,i)),{...n,pageCount:i}}),e.getPageOptions=i(()=>[e.getPageCount()],e=>{let t=[];return e&&e>0&&(t=[...Array(e)].fill(null).map((e,t)=>t)),t},r(e.options,"debugTable","getPageOptions")),e.getCanPreviousPage=()=>e.getState().pagination.pageIndex>0,e.getCanNextPage=()=>{let{pageIndex:t}=e.getState().pagination,l=e.getPageCount();return -1===l||0!==l&&te.setPageIndex(e=>e-1),e.nextPage=()=>e.setPageIndex(e=>e+1),e.firstPage=()=>e.setPageIndex(0),e.lastPage=()=>e.setPageIndex(e.getPageCount()-1),e.getPrePaginationRowModel=()=>e.getExpandedRowModel(),e.getPaginationRowModel=()=>(!e._getPaginationRowModel&&e.options.getPaginationRowModel&&(e._getPaginationRowModel=e.options.getPaginationRowModel(e)),e.options.manualPagination||!e._getPaginationRowModel)?e.getPrePaginationRowModel():e._getPaginationRowModel(),e.getPageCount=()=>{var t;return null!=(t=e.options.pageCount)?t:Math.ceil(e.getRowCount()/e.getState().pagination.pageSize)},e.getRowCount=()=>{var t;return null!=(t=e.options.rowCount)?t:e.getPrePaginationRowModel().rows.length}}},{getInitialState:e=>({rowPinning:E(),...e}),getDefaultOptions:e=>({onRowPinningChange:n("rowPinning",e)}),createRow:(e,t)=>{e.pin=(l,n,o)=>{let i=n?e.getLeafRows().map(e=>{let{id:t}=e;return t}):[],r=new Set([...o?e.getParentRows().map(e=>{let{id:t}=e;return t}):[],e.id,...i]);t.setRowPinning(e=>{var t,n,o,i,a,u;return"bottom"===l?{top:(null!=(o=null==e?void 0:e.top)?o:[]).filter(e=>!(null!=r&&r.has(e))),bottom:[...(null!=(i=null==e?void 0:e.bottom)?i:[]).filter(e=>!(null!=r&&r.has(e))),...Array.from(r)]}:"top"===l?{top:[...(null!=(a=null==e?void 0:e.top)?a:[]).filter(e=>!(null!=r&&r.has(e))),...Array.from(r)],bottom:(null!=(u=null==e?void 0:e.bottom)?u:[]).filter(e=>!(null!=r&&r.has(e)))}:{top:(null!=(t=null==e?void 0:e.top)?t:[]).filter(e=>!(null!=r&&r.has(e))),bottom:(null!=(n=null==e?void 0:e.bottom)?n:[]).filter(e=>!(null!=r&&r.has(e)))}})},e.getCanPin=()=>{var l;let{enableRowPinning:n,enablePinning:o}=t.options;return"function"==typeof n?n(e):null==(l=null!=n?n:o)||l},e.getIsPinned=()=>{let l=[e.id],{top:n,bottom:o}=t.getState().rowPinning,i=l.some(e=>null==n?void 0:n.includes(e)),r=l.some(e=>null==o?void 0:o.includes(e));return i?"top":!!r&&"bottom"},e.getPinnedIndex=()=>{var l,n;let o=e.getIsPinned();if(!o)return -1;let i=null==(l="top"===o?t.getTopRows():t.getBottomRows())?void 0:l.map(e=>{let{id:t}=e;return t});return null!=(n=null==i?void 0:i.indexOf(e.id))?n:-1}},createTable:e=>{e.setRowPinning=t=>null==e.options.onRowPinningChange?void 0:e.options.onRowPinningChange(t),e.resetRowPinning=t=>{var l,n;return e.setRowPinning(t?E():null!=(l=null==(n=e.initialState)?void 0:n.rowPinning)?l:E())},e.getIsSomeRowsPinned=t=>{var l,n,o;let i=e.getState().rowPinning;return t?!!(null==(l=i[t])?void 0:l.length):!!((null==(n=i.top)?void 0:n.length)||(null==(o=i.bottom)?void 0:o.length))},e._getPinnedRows=(t,l,n)=>{var o;return(null==(o=e.options.keepPinnedRows)||o?(null!=l?l:[]).map(t=>{let l=e.getRow(t,!0);return l.getIsAllParentsExpanded()?l:null}):(null!=l?l:[]).map(e=>t.find(t=>t.id===e))).filter(Boolean).map(e=>({...e,position:n}))},e.getTopRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.top],(t,l)=>e._getPinnedRows(t,l,"top"),r(e.options,"debugRows","getTopRows")),e.getBottomRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.bottom],(t,l)=>e._getPinnedRows(t,l,"bottom"),r(e.options,"debugRows","getBottomRows")),e.getCenterRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.top,e.getState().rowPinning.bottom],(e,t,l)=>{let n=new Set([...null!=t?t:[],...null!=l?l:[]]);return e.filter(e=>!n.has(e.id))},r(e.options,"debugRows","getCenterRows"))}},{getInitialState:e=>({rowSelection:{},...e}),getDefaultOptions:e=>({onRowSelectionChange:n("rowSelection",e),enableRowSelection:!0,enableMultiRowSelection:!0,enableSubRowSelection:!0}),createTable:e=>{e.setRowSelection=t=>null==e.options.onRowSelectionChange?void 0:e.options.onRowSelectionChange(t),e.resetRowSelection=t=>{var l;return e.setRowSelection(t?{}:null!=(l=e.initialState.rowSelection)?l:{})},e.toggleAllRowsSelected=t=>{e.setRowSelection(l=>{t=void 0!==t?t:!e.getIsAllRowsSelected();let n={...l},o=e.getPreGroupedRowModel().flatRows;return t?o.forEach(e=>{e.getCanSelect()&&(n[e.id]=!0)}):o.forEach(e=>{delete n[e.id]}),n})},e.toggleAllPageRowsSelected=t=>e.setRowSelection(l=>{let n=void 0!==t?t:!e.getIsAllPageRowsSelected(),o={...l};return e.getRowModel().rows.forEach(t=>{G(o,t.id,n,!0,e)}),o}),e.getPreSelectedRowModel=()=>e.getCoreRowModel(),e.getSelectedRowModel=i(()=>[e.getState().rowSelection,e.getCoreRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getSelectedRowModel")),e.getFilteredSelectedRowModel=i(()=>[e.getState().rowSelection,e.getFilteredRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getFilteredSelectedRowModel")),e.getGroupedSelectedRowModel=i(()=>[e.getState().rowSelection,e.getSortedRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getGroupedSelectedRowModel")),e.getIsAllRowsSelected=()=>{let t=e.getFilteredRowModel().flatRows,{rowSelection:l}=e.getState(),n=!!(t.length&&Object.keys(l).length);return n&&t.some(e=>e.getCanSelect()&&!l[e.id])&&(n=!1),n},e.getIsAllPageRowsSelected=()=>{let t=e.getPaginationRowModel().flatRows.filter(e=>e.getCanSelect()),{rowSelection:l}=e.getState(),n=!!t.length;return n&&t.some(e=>!l[e.id])&&(n=!1),n},e.getIsSomeRowsSelected=()=>{var t;let l=Object.keys(null!=(t=e.getState().rowSelection)?t:{}).length;return l>0&&l{let t=e.getPaginationRowModel().flatRows;return!e.getIsAllPageRowsSelected()&&t.filter(e=>e.getCanSelect()).some(e=>e.getIsSelected()||e.getIsSomeSelected())},e.getToggleAllRowsSelectedHandler=()=>t=>{e.toggleAllRowsSelected(t.target.checked)},e.getToggleAllPageRowsSelectedHandler=()=>t=>{e.toggleAllPageRowsSelected(t.target.checked)}},createRow:(e,t)=>{e.toggleSelected=(l,n)=>{let o=e.getIsSelected();t.setRowSelection(i=>{var r;if(l=void 0!==l?l:!o,e.getCanSelect()&&o===l)return i;let a={...i};return G(a,e.id,l,null==(r=null==n?void 0:n.selectChildren)||r,t),a})},e.getIsSelected=()=>{let{rowSelection:l}=t.getState();return A(e,l)},e.getIsSomeSelected=()=>{let{rowSelection:l}=t.getState();return"some"===H(e,l)},e.getIsAllSubRowsSelected=()=>{let{rowSelection:l}=t.getState();return"all"===H(e,l)},e.getCanSelect=()=>{var l;return"function"==typeof t.options.enableRowSelection?t.options.enableRowSelection(e):null==(l=t.options.enableRowSelection)||l},e.getCanSelectSubRows=()=>{var l;return"function"==typeof t.options.enableSubRowSelection?t.options.enableSubRowSelection(e):null==(l=t.options.enableSubRowSelection)||l},e.getCanMultiSelect=()=>{var l;return"function"==typeof t.options.enableMultiRowSelection?t.options.enableMultiRowSelection(e):null==(l=t.options.enableMultiRowSelection)||l},e.getToggleSelectedHandler=()=>{let t=e.getCanSelect();return l=>{var n;t&&e.toggleSelected(null==(n=l.target)?void 0:n.checked)}}}},{getDefaultColumnDef:()=>V,getInitialState:e=>({columnSizing:{},columnSizingInfo:P(),...e}),getDefaultOptions:e=>({columnResizeMode:"onEnd",columnResizeDirection:"ltr",onColumnSizingChange:n("columnSizing",e),onColumnSizingInfoChange:n("columnSizingInfo",e)}),createColumn:(e,t)=>{e.getSize=()=>{var l,n,o;let i=t.getState().columnSizing[e.id];return Math.min(Math.max(null!=(l=e.columnDef.minSize)?l:V.minSize,null!=(n=null!=i?i:e.columnDef.size)?n:V.size),null!=(o=e.columnDef.maxSize)?o:V.maxSize)},e.getStart=i(e=>[e,_(t,e),t.getState().columnSizing],(t,l)=>l.slice(0,e.getIndex(t)).reduce((e,t)=>e+t.getSize(),0),r(t.options,"debugColumns","getStart")),e.getAfter=i(e=>[e,_(t,e),t.getState().columnSizing],(t,l)=>l.slice(e.getIndex(t)+1).reduce((e,t)=>e+t.getSize(),0),r(t.options,"debugColumns","getAfter")),e.resetSize=()=>{t.setColumnSizing(t=>{let{[e.id]:l,...n}=t;return n})},e.getCanResize=()=>{var l,n;return(null==(l=e.columnDef.enableResizing)||l)&&(null==(n=t.options.enableColumnResizing)||n)},e.getIsResizing=()=>t.getState().columnSizingInfo.isResizingColumn===e.id},createHeader:(e,t)=>{e.getSize=()=>{let t=0,l=e=>{if(e.subHeaders.length)e.subHeaders.forEach(l);else{var n;t+=null!=(n=e.column.getSize())?n:0}};return l(e),t},e.getStart=()=>{if(e.index>0){let t=e.headerGroup.headers[e.index-1];return t.getStart()+t.getSize()}return 0},e.getResizeHandler=l=>{let n=t.getColumn(e.column.id),o=null==n?void 0:n.getCanResize();return i=>{if(!n||!o||(null==i.persist||i.persist(),x(i)&&i.touches&&i.touches.length>1))return;let r=e.getSize(),a=e?e.getLeafHeaders().map(e=>[e.column.id,e.column.getSize()]):[[n.id,n.getSize()]],u=x(i)?Math.round(i.touches[0].clientX):i.clientX,g={},s=(e,l)=>{"number"==typeof l&&(t.setColumnSizingInfo(e=>{var n,o;let i="rtl"===t.options.columnResizeDirection?-1:1,r=(l-(null!=(n=null==e?void 0:e.startOffset)?n:0))*i,a=Math.max(r/(null!=(o=null==e?void 0:e.startSize)?o:0),-.999999);return e.columnSizingStart.forEach(e=>{let[t,l]=e;g[t]=Math.round(100*Math.max(l+l*a,0))/100}),{...e,deltaOffset:r,deltaPercentage:a}}),("onChange"===t.options.columnResizeMode||"end"===e)&&t.setColumnSizing(e=>({...e,...g})))},d=e=>{s("end",e),t.setColumnSizingInfo(e=>({...e,isResizingColumn:!1,startOffset:null,startSize:null,deltaOffset:null,deltaPercentage:null,columnSizingStart:[]}))},p=l||("u">typeof document?document:null),c={moveHandler:e=>s("move",e.clientX),upHandler:e=>{null==p||p.removeEventListener("mousemove",c.moveHandler),null==p||p.removeEventListener("mouseup",c.upHandler),d(e.clientX)}},f={moveHandler:e=>(e.cancelable&&(e.preventDefault(),e.stopPropagation()),s("move",e.touches[0].clientX),!1),upHandler:e=>{var t;null==p||p.removeEventListener("touchmove",f.moveHandler),null==p||p.removeEventListener("touchend",f.upHandler),e.cancelable&&(e.preventDefault(),e.stopPropagation()),d(null==(t=e.touches[0])?void 0:t.clientX)}},m=!!function(){if("boolean"==typeof I)return I;let e=!1;try{let t=()=>{};window.addEventListener("test",t,{get passive(){return e=!0,!1}}),window.removeEventListener("test",t)}catch(t){e=!1}return I=e}()&&{passive:!1};x(i)?(null==p||p.addEventListener("touchmove",f.moveHandler,m),null==p||p.addEventListener("touchend",f.upHandler,m)):(null==p||p.addEventListener("mousemove",c.moveHandler,m),null==p||p.addEventListener("mouseup",c.upHandler,m)),t.setColumnSizingInfo(e=>({...e,startOffset:u,startSize:r,deltaOffset:0,deltaPercentage:0,columnSizingStart:a,isResizingColumn:n.id}))}}},createTable:e=>{e.setColumnSizing=t=>null==e.options.onColumnSizingChange?void 0:e.options.onColumnSizingChange(t),e.setColumnSizingInfo=t=>null==e.options.onColumnSizingInfoChange?void 0:e.options.onColumnSizingInfoChange(t),e.resetColumnSizing=t=>{var l;e.setColumnSizing(t?{}:null!=(l=e.initialState.columnSizing)?l:{})},e.resetHeaderSizeInfo=t=>{var l;e.setColumnSizingInfo(t?P():null!=(l=e.initialState.columnSizingInfo)?l:P())},e.getTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getLeftTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getLeftHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getCenterTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getCenterHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getRightTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getRightHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0}}}];function j(e){var t,n;let o=[...q,...null!=(t=e._features)?t:[]],a={_features:o},u=a._features.reduce((e,t)=>Object.assign(e,null==t.getDefaultOptions?void 0:t.getDefaultOptions(a)),{}),g={...null!=(n=e.initialState)?n:{}};a._features.forEach(e=>{var t;g=null!=(t=null==e.getInitialState?void 0:e.getInitialState(g))?t:g});let s=[],d=!1,p={_features:o,options:{...u,...e},initialState:g,_queue:e=>{s.push(e),d||(d=!0,Promise.resolve().then(()=>{for(;s.length;)s.shift()();d=!1}).catch(e=>setTimeout(()=>{throw e})))},reset:()=>{a.setState(a.initialState)},setOptions:e=>{var t;t=l(e,a.options),a.options=a.options.mergeOptions?a.options.mergeOptions(u,t):{...u,...t}},getState:()=>a.options.state,setState:e=>{null==a.options.onStateChange||a.options.onStateChange(e)},_getRowId:(e,t,l)=>{var n;return null!=(n=null==a.options.getRowId?void 0:a.options.getRowId(e,t,l))?n:`${l?[l.id,t].join("."):t}`},getCoreRowModel:()=>(a._getCoreRowModel||(a._getCoreRowModel=a.options.getCoreRowModel(a)),a._getCoreRowModel()),getRowModel:()=>a.getPaginationRowModel(),getRow:(e,t)=>{let l=(t?a.getPrePaginationRowModel():a.getRowModel()).rowsById[e];if(!l&&!(l=a.getCoreRowModel().rowsById[e]))throw Error();return l},_getDefaultColumnDef:i(()=>[a.options.defaultColumn],e=>{var t;return e=null!=(t=e)?t:{},{header:e=>{let t=e.header.column.columnDef;return t.accessorKey?t.accessorKey:t.accessorFn?t.id:null},cell:e=>{var t,l;return null!=(t=null==(l=e.renderValue())||null==l.toString?void 0:l.toString())?t:null},...a._features.reduce((e,t)=>Object.assign(e,null==t.getDefaultColumnDef?void 0:t.getDefaultColumnDef()),{}),...e}},r(e,"debugColumns","_getDefaultColumnDef")),_getColumnDefs:()=>a.options.columns,getAllColumns:i(()=>[a._getColumnDefs()],e=>{let t=function(e,l,n){return void 0===n&&(n=0),e.map(e=>{let o=function(e,t,l,n){var o,a;let u,g={...e._getDefaultColumnDef(),...t},s=g.accessorKey,d=null!=(o=null!=(a=g.id)?a:s?"function"==typeof String.prototype.replaceAll?s.replaceAll(".","_"):s.replace(/\./g,"_"):void 0)?o:"string"==typeof g.header?g.header:void 0;if(g.accessorFn?u=g.accessorFn:s&&(u=s.includes(".")?e=>{let t=e;for(let e of s.split(".")){var l;t=null==(l=t)?void 0:l[e]}return t}:e=>e[g.accessorKey]),!d)throw Error();let p={id:`${String(d)}`,accessorFn:u,parent:n,depth:l,columnDef:g,columns:[],getFlatColumns:i(()=>[!0],()=>{var e;return[p,...null==(e=p.columns)?void 0:e.flatMap(e=>e.getFlatColumns())]},r(e.options,"debugColumns","column.getFlatColumns")),getLeafColumns:i(()=>[e._getOrderColumnsFn()],e=>{var t;return null!=(t=p.columns)&&t.length?e(p.columns.flatMap(e=>e.getLeafColumns())):[p]},r(e.options,"debugColumns","column.getLeafColumns"))};for(let t of e._features)null==t.createColumn||t.createColumn(p,e);return p}(a,e,n,l);return o.columns=e.columns?t(e.columns,o,n+1):[],o})};return t(e)},r(e,"debugColumns","getAllColumns")),getAllFlatColumns:i(()=>[a.getAllColumns()],e=>e.flatMap(e=>e.getFlatColumns()),r(e,"debugColumns","getAllFlatColumns")),_getAllFlatColumnsById:i(()=>[a.getAllFlatColumns()],e=>e.reduce((e,t)=>(e[t.id]=t,e),{}),r(e,"debugColumns","getAllFlatColumnsById")),getAllLeafColumns:i(()=>[a.getAllColumns(),a._getOrderColumnsFn()],(e,t)=>t(e.flatMap(e=>e.getLeafColumns())),r(e,"debugColumns","getAllLeafColumns")),getColumn:e=>a._getAllFlatColumnsById()[e]};Object.assign(a,p);for(let e=0;e{var n;t.push(e),null!=(n=e.subRows)&&n.length&&e.getIsExpanded()&&e.subRows.forEach(l)};return e.rows.forEach(l),{rows:t,flatRows:e.flatRows,rowsById:e.rowsById}}e.s(["createTable",0,j,"getCoreRowModel",0,function(){return e=>i(()=>[e.options.data],t=>{let l={rows:[],flatRows:[],rowsById:{}},n=function(t,o,i){void 0===o&&(o=0);let r=[];for(let u=0;ue._autoResetPageIndex()))},"getExpandedRowModel",0,function(){return e=>i(()=>[e.getState().expanded,e.getPreExpandedRowModel(),e.options.paginateExpandedRows],(e,t,l)=>t.rows.length&&(!0===e||Object.keys(null!=e?e:{}).length)&&l?k(t):t,r(e.options,"debugTable","getExpandedRowModel"))},"getPaginationRowModel",0,function(e){return e=>i(()=>[e.getState().pagination,e.getPrePaginationRowModel(),e.options.paginateExpandedRows?void 0:e.getState().expanded],(t,l)=>{let n;if(!l.rows.length)return l;let{pageSize:o,pageIndex:i}=t,{rows:r,flatRows:a,rowsById:u}=l,g=o*i;r=r.slice(g,g+o),(n=e.options.paginateExpandedRows?{rows:r,flatRows:a,rowsById:u}:k({rows:r,flatRows:a,rowsById:u})).flatRows=[];let s=e=>{n.flatRows.push(e),e.subRows.length&&e.subRows.forEach(s)};return n.rows.forEach(s),n},r(e.options,"debugTable","getPaginationRowModel"))},"getSortedRowModel",0,function(){return e=>i(()=>[e.getState().sorting,e.getPreSortedRowModel()],(t,l)=>{if(!l.rows.length||!(null!=t&&t.length))return l;let n=e.getState().sorting,o=[],i=n.filter(t=>{var l;return null==(l=e.getColumn(t.id))?void 0:l.getCanSort()}),r={};i.forEach(t=>{let l=e.getColumn(t.id);l&&(r[t.id]={sortUndefined:l.columnDef.sortUndefined,invertSorting:l.columnDef.invertSorting,sortingFn:l.getSortingFn()})});let a=e=>{let t=e.map(e=>({...e}));return t.sort((e,t)=>{for(let n=0;n{var t;o.push(e),null!=(t=e.subRows)&&t.length&&(e.subRows=a(e.subRows))}),t};return{rows:a(l.rows),flatRows:o,rowsById:l.rowsById}},r(e.options,"debugTable","getSortedRowModel",()=>e._autoResetPageIndex()))}],682830),e.s(["flexRender",0,function(e,l){var n,o,i;let r;return e?"function"==typeof(o=n=e)&&(r=Object.getPrototypeOf(o)).prototype&&r.prototype.isReactComponent||"function"==typeof n||"object"==typeof(i=n)&&"symbol"==typeof i.$$typeof&&["react.memo","react.forward_ref"].includes(i.$$typeof.description)?t.createElement(e,l):e:null},"useReactTable",0,function(e){let l={state:{},onStateChange:()=>{},renderFallbackValue:null,...e},[n]=t.useState(()=>({current:j(l)})),[o,i]=t.useState(()=>n.current.initialState);return n.current.setOptions(t=>({...t,...e,state:{...o,...e.state},onStateChange:t=>{i(t),null==e.onStateChange||e.onStateChange(t)}})),n.current}],152990)}]); \ No newline at end of file + color: hsl(${Math.max(0,Math.min(120-120*n,120))}deg 100% 31%);`,null==l?void 0:l.key)}return n}}function r(e,t,l,n){return{debug:()=>{var l;return null!=(l=null==e?void 0:e.debugAll)?l:e[t]},key:!1,onChange:n}}e.i(247167);let a="debugHeaders";function u(e,t,l){var n;let o={id:null!=(n=l.id)?n:t.id,column:t,index:l.index,isPlaceholder:!!l.isPlaceholder,placeholderId:l.placeholderId,depth:l.depth,subHeaders:[],colSpan:0,rowSpan:0,headerGroup:null,getLeafHeaders:()=>{let e=[],t=l=>{l.subHeaders&&l.subHeaders.length&&l.subHeaders.map(t),e.push(l)};return t(o),e},getContext:()=>({table:e,header:o,column:t})};return e._features.forEach(t=>{null==t.createHeader||t.createHeader(o,e)}),o}function g(e,t,l,n){var o,i;let r=0,a=function(e,t){void 0===t&&(t=1),r=Math.max(r,t),e.filter(e=>e.getIsVisible()).forEach(e=>{var l;null!=(l=e.columns)&&l.length&&a(e.columns,t+1)},0)};a(e);let g=[],s=(e,t)=>{let o={depth:t,id:[n,`${t}`].filter(Boolean).join("_"),headers:[]},i=[];e.forEach(e=>{let r,a=[...i].reverse()[0],g=e.column.depth===o.depth,s=!1;if(g&&e.column.parent?r=e.column.parent:(r=e.column,s=!0),a&&(null==a?void 0:a.column)===r)a.subHeaders.push(e);else{let o=u(l,r,{id:[n,t,r.id,null==e?void 0:e.id].filter(Boolean).join("_"),isPlaceholder:s,placeholderId:s?`${i.filter(e=>e.column===r).length}`:void 0,depth:t,index:i.length});o.subHeaders.push(e),i.push(o)}o.headers.push(e),e.headerGroup=o}),g.push(o),t>0&&s(i,t-1)};s(t.map((e,t)=>u(l,e,{depth:r,index:t})),r-1),g.reverse();let d=e=>e.filter(e=>e.column.getIsVisible()).map(e=>{let t=0,l=0,n=[0];return e.subHeaders&&e.subHeaders.length?(n=[],d(e.subHeaders).forEach(e=>{let{colSpan:l,rowSpan:o}=e;t+=l,n.push(o)})):t=1,l+=Math.min(...n),e.colSpan=t,e.rowSpan=l,{colSpan:t,rowSpan:l}});return d(null!=(o=null==(i=g[0])?void 0:i.headers)?o:[]),g}let s=(e,t,l,n,o,a,u)=>{let g={id:t,index:n,original:l,depth:o,parentId:u,_valuesCache:{},_uniqueValuesCache:{},getValue:t=>{if(g._valuesCache.hasOwnProperty(t))return g._valuesCache[t];let l=e.getColumn(t);if(null!=l&&l.accessorFn)return g._valuesCache[t]=l.accessorFn(g.original,n),g._valuesCache[t]},getUniqueValues:t=>{if(g._uniqueValuesCache.hasOwnProperty(t))return g._uniqueValuesCache[t];let l=e.getColumn(t);if(null!=l&&l.accessorFn)return l.columnDef.getUniqueValues?g._uniqueValuesCache[t]=l.columnDef.getUniqueValues(g.original,n):g._uniqueValuesCache[t]=[g.getValue(t)],g._uniqueValuesCache[t]},renderValue:t=>{var l;return null!=(l=g.getValue(t))?l:e.options.renderFallbackValue},subRows:null!=a?a:[],getLeafRows:()=>{var e,t;let l,n;return e=g.subRows,t=e=>e.subRows,l=[],(n=e=>{e.forEach(e=>{l.push(e);let o=t(e);null!=o&&o.length&&n(o)})})(e),l},getParentRow:()=>g.parentId?e.getRow(g.parentId,!0):void 0,getParentRows:()=>{let e=[],t=g;for(;;){let l=t.getParentRow();if(!l)break;e.push(l),t=l}return e.reverse()},getAllCells:i(()=>[e.getAllLeafColumns()],t=>t.map(t=>{var l;let n;return l=t.id,n={id:`${g.id}_${t.id}`,row:g,column:t,getValue:()=>g.getValue(l),renderValue:()=>{var t;return null!=(t=n.getValue())?t:e.options.renderFallbackValue},getContext:i(()=>[e,t,g,n],(e,t,l,n)=>({table:e,column:t,row:l,cell:n,getValue:n.getValue,renderValue:n.renderValue}),r(e.options,"debugCells","cell.getContext"))},e._features.forEach(l=>{null==l.createCell||l.createCell(n,t,g,e)},{}),n}),r(e.options,"debugRows","getAllCells")),_getAllCellsByColumnId:i(()=>[g.getAllCells()],e=>e.reduce((e,t)=>(e[t.column.id]=t,e),{}),r(e.options,"debugRows","getAllCellsByColumnId"))};for(let t=0;t{var n,o;let i=null==l||null==(n=l.toString())?void 0:n.toLowerCase();return!!(null==(o=e.getValue(t))||null==(o=o.toString())||null==(o=o.toLowerCase())?void 0:o.includes(i))};d.autoRemove=e=>S(e);let p=(e,t,l)=>{var n;return!!(null==(n=e.getValue(t))||null==(n=n.toString())?void 0:n.includes(l))};p.autoRemove=e=>S(e);let c=(e,t,l)=>{var n;return(null==(n=e.getValue(t))||null==(n=n.toString())?void 0:n.toLowerCase())===(null==l?void 0:l.toLowerCase())};c.autoRemove=e=>S(e);let f=(e,t,l)=>{var n;return null==(n=e.getValue(t))?void 0:n.includes(l)};f.autoRemove=e=>S(e);let m=(e,t,l)=>!l.some(l=>{var n;return!(null!=(n=e.getValue(t))&&n.includes(l))});m.autoRemove=e=>S(e)||!(null!=e&&e.length);let C=(e,t,l)=>l.some(l=>{var n;return null==(n=e.getValue(t))?void 0:n.includes(l)});C.autoRemove=e=>S(e)||!(null!=e&&e.length);let w=(e,t,l)=>e.getValue(t)===l;w.autoRemove=e=>S(e);let R=(e,t,l)=>e.getValue(t)==l;R.autoRemove=e=>S(e);let h=(e,t,l)=>{let[n,o]=l,i=e.getValue(t);return i>=n&&i<=o};h.resolveFilterValue=e=>{let[t,l]=e,n="number"!=typeof t?parseFloat(t):t,o="number"!=typeof l?parseFloat(l):l,i=null===t||Number.isNaN(n)?-1/0:n,r=null===l||Number.isNaN(o)?1/0:o;if(i>r){let e=i;i=r,r=e}return[i,r]},h.autoRemove=e=>S(e)||S(e[0])&&S(e[1]);let v={includesString:d,includesStringSensitive:p,equalsString:c,arrIncludes:f,arrIncludesAll:m,arrIncludesSome:C,equals:w,weakEquals:R,inNumberRange:h};function S(e){return null==e||""===e}function b(e,t,l){return!!e&&!!e.autoRemove&&e.autoRemove(t,l)||void 0===t||"string"==typeof t&&!t}let F={sum:(e,t,l)=>l.reduce((t,l)=>{let n=l.getValue(e);return t+("number"==typeof n?n:0)},0),min:(e,t,l)=>{let n;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(n>l||void 0===n&&l>=l)&&(n=l)}),n},max:(e,t,l)=>{let n;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(n=l)&&(n=l)}),n},extent:(e,t,l)=>{let n,o;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(void 0===n?l>=l&&(n=o=l):(n>l&&(n=l),o{let l=0,n=0;if(t.forEach(t=>{let o=t.getValue(e);null!=o&&(o*=1)>=o&&(++l,n+=o)}),l)return n/l},median:(e,t)=>{if(!t.length)return;let l=t.map(t=>t.getValue(e));if(!(Array.isArray(l)&&l.every(e=>"number"==typeof e)))return;if(1===l.length)return l[0];let n=Math.floor(l.length/2),o=l.sort((e,t)=>e-t);return l.length%2!=0?o[n]:(o[n-1]+o[n])/2},unique:(e,t)=>Array.from(new Set(t.map(t=>t.getValue(e))).values()),uniqueCount:(e,t)=>new Set(t.map(t=>t.getValue(e))).size,count:(e,t)=>t.length},M=()=>({left:[],right:[]}),V={size:150,minSize:20,maxSize:Number.MAX_SAFE_INTEGER},P=()=>({startOffset:null,startSize:null,deltaOffset:null,deltaPercentage:null,isResizingColumn:!1,columnSizingStart:[]}),I=null;function x(e){return"touchstart"===e.type}function _(e,t){return t?"center"===t?e.getCenterVisibleLeafColumns():"left"===t?e.getLeftVisibleLeafColumns():e.getRightVisibleLeafColumns():e.getVisibleLeafColumns()}let y=()=>({pageIndex:0,pageSize:10}),E=()=>({top:[],bottom:[]}),G=(e,t,l,n,o)=>{var i;let r=o.getRow(t,!0);l?(r.getCanMultiSelect()||Object.keys(e).forEach(t=>delete e[t]),r.getCanSelect()&&(e[t]=!0)):delete e[t],n&&null!=(i=r.subRows)&&i.length&&r.getCanSelectSubRows()&&r.subRows.forEach(t=>G(e,t.id,l,n,o))};function L(e,t){let l=e.getState().rowSelection,n=[],o={},i=function(e,t){return e.map(e=>{var t;let r=A(e,l);if(r&&(n.push(e),o[e.id]=e),null!=(t=e.subRows)&&t.length&&(e={...e,subRows:i(e.subRows)}),r)return e}).filter(Boolean)};return{rows:i(t.rows),flatRows:n,rowsById:o}}function A(e,t){var l;return null!=(l=t[e.id])&&l}function H(e,t,l){var n;if(!(null!=(n=e.subRows)&&n.length))return!1;let o=!0,i=!1;return e.subRows.forEach(e=>{if((!i||o)&&(e.getCanSelect()&&(A(e,t)?i=!0:o=!1),e.subRows&&e.subRows.length)){let l=H(e,t);"all"===l?i=!0:("some"===l&&(i=!0),o=!1)}}),o?"all":!!i&&"some"}let D=/([0-9]+)/gm;function z(e,t){return e===t?0:e>t?1:-1}function O(e){return"number"==typeof e?isNaN(e)||e===1/0||e===-1/0?"":String(e):"string"==typeof e?e:""}function T(e,t){let l=e.split(D).filter(Boolean),n=t.split(D).filter(Boolean);for(;l.length&&n.length;){let e=l.shift(),t=n.shift(),o=parseInt(e,10),i=parseInt(t,10),r=[o,i].sort();if(isNaN(r[0])){if(e>t)return 1;if(t>e)return -1;continue}if(isNaN(r[1]))return isNaN(o)?-1:1;if(o>i)return 1;if(i>o)return -1}return l.length-n.length}let B={alphanumeric:(e,t,l)=>T(O(e.getValue(l)).toLowerCase(),O(t.getValue(l)).toLowerCase()),alphanumericCaseSensitive:(e,t,l)=>T(O(e.getValue(l)),O(t.getValue(l))),text:(e,t,l)=>z(O(e.getValue(l)).toLowerCase(),O(t.getValue(l)).toLowerCase()),textCaseSensitive:(e,t,l)=>z(O(e.getValue(l)),O(t.getValue(l))),datetime:(e,t,l)=>{let n=e.getValue(l),o=t.getValue(l);return n>o?1:nz(e.getValue(l),t.getValue(l))},q=[{createTable:e=>{e.getHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(t,l,n,o)=>{var i,r;let a=null!=(i=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?i:[],u=null!=(r=null==o?void 0:o.map(e=>l.find(t=>t.id===e)).filter(Boolean))?r:[];return g(t,[...a,...l.filter(e=>!(null!=n&&n.includes(e.id))&&!(null!=o&&o.includes(e.id))),...u],e)},r(e.options,a,"getHeaderGroups")),e.getCenterHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(t,l,n,o)=>g(t,l=l.filter(e=>!(null!=n&&n.includes(e.id))&&!(null!=o&&o.includes(e.id))),e,"center"),r(e.options,a,"getCenterHeaderGroups")),e.getLeftHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left],(t,l,n)=>{var o;return g(t,null!=(o=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?o:[],e,"left")},r(e.options,a,"getLeftHeaderGroups")),e.getRightHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.right],(t,l,n)=>{var o;return g(t,null!=(o=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?o:[],e,"right")},r(e.options,a,"getRightHeaderGroups")),e.getFooterGroups=i(()=>[e.getHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getFooterGroups")),e.getLeftFooterGroups=i(()=>[e.getLeftHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getLeftFooterGroups")),e.getCenterFooterGroups=i(()=>[e.getCenterHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getCenterFooterGroups")),e.getRightFooterGroups=i(()=>[e.getRightHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getRightFooterGroups")),e.getFlatHeaders=i(()=>[e.getHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getFlatHeaders")),e.getLeftFlatHeaders=i(()=>[e.getLeftHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getLeftFlatHeaders")),e.getCenterFlatHeaders=i(()=>[e.getCenterHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getCenterFlatHeaders")),e.getRightFlatHeaders=i(()=>[e.getRightHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getRightFlatHeaders")),e.getCenterLeafHeaders=i(()=>[e.getCenterFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getCenterLeafHeaders")),e.getLeftLeafHeaders=i(()=>[e.getLeftFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getLeftLeafHeaders")),e.getRightLeafHeaders=i(()=>[e.getRightFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getRightLeafHeaders")),e.getLeafHeaders=i(()=>[e.getLeftHeaderGroups(),e.getCenterHeaderGroups(),e.getRightHeaderGroups()],(e,t,l)=>{var n,o,i,r,a,u;return[...null!=(n=null==(o=e[0])?void 0:o.headers)?n:[],...null!=(i=null==(r=t[0])?void 0:r.headers)?i:[],...null!=(a=null==(u=l[0])?void 0:u.headers)?a:[]].map(e=>e.getLeafHeaders()).flat()},r(e.options,a,"getLeafHeaders"))}},{getInitialState:e=>({columnVisibility:{},...e}),getDefaultOptions:e=>({onColumnVisibilityChange:n("columnVisibility",e)}),createColumn:(e,t)=>{e.toggleVisibility=l=>{e.getCanHide()&&t.setColumnVisibility(t=>({...t,[e.id]:null!=l?l:!e.getIsVisible()}))},e.getIsVisible=()=>{var l,n;let o=e.columns;return null==(l=o.length?o.some(e=>e.getIsVisible()):null==(n=t.getState().columnVisibility)?void 0:n[e.id])||l},e.getCanHide=()=>{var l,n;return(null==(l=e.columnDef.enableHiding)||l)&&(null==(n=t.options.enableHiding)||n)},e.getToggleVisibilityHandler=()=>t=>{null==e.toggleVisibility||e.toggleVisibility(t.target.checked)}},createRow:(e,t)=>{e._getAllVisibleCells=i(()=>[e.getAllCells(),t.getState().columnVisibility],e=>e.filter(e=>e.column.getIsVisible()),r(t.options,"debugRows","_getAllVisibleCells")),e.getVisibleCells=i(()=>[e.getLeftVisibleCells(),e.getCenterVisibleCells(),e.getRightVisibleCells()],(e,t,l)=>[...e,...t,...l],r(t.options,"debugRows","getVisibleCells"))},createTable:e=>{let t=(t,l)=>i(()=>[l(),l().filter(e=>e.getIsVisible()).map(e=>e.id).join("_")],e=>e.filter(e=>null==e.getIsVisible?void 0:e.getIsVisible()),r(e.options,"debugColumns",t));e.getVisibleFlatColumns=t("getVisibleFlatColumns",()=>e.getAllFlatColumns()),e.getVisibleLeafColumns=t("getVisibleLeafColumns",()=>e.getAllLeafColumns()),e.getLeftVisibleLeafColumns=t("getLeftVisibleLeafColumns",()=>e.getLeftLeafColumns()),e.getRightVisibleLeafColumns=t("getRightVisibleLeafColumns",()=>e.getRightLeafColumns()),e.getCenterVisibleLeafColumns=t("getCenterVisibleLeafColumns",()=>e.getCenterLeafColumns()),e.setColumnVisibility=t=>null==e.options.onColumnVisibilityChange?void 0:e.options.onColumnVisibilityChange(t),e.resetColumnVisibility=t=>{var l;e.setColumnVisibility(t?{}:null!=(l=e.initialState.columnVisibility)?l:{})},e.toggleAllColumnsVisible=t=>{var l;t=null!=(l=t)?l:!e.getIsAllColumnsVisible(),e.setColumnVisibility(e.getAllLeafColumns().reduce((e,l)=>({...e,[l.id]:t||!(null!=l.getCanHide&&l.getCanHide())}),{}))},e.getIsAllColumnsVisible=()=>!e.getAllLeafColumns().some(e=>!(null!=e.getIsVisible&&e.getIsVisible())),e.getIsSomeColumnsVisible=()=>e.getAllLeafColumns().some(e=>null==e.getIsVisible?void 0:e.getIsVisible()),e.getToggleAllColumnsVisibilityHandler=()=>t=>{var l;e.toggleAllColumnsVisible(null==(l=t.target)?void 0:l.checked)}}},{getInitialState:e=>({columnOrder:[],...e}),getDefaultOptions:e=>({onColumnOrderChange:n("columnOrder",e)}),createColumn:(e,t)=>{e.getIndex=i(e=>[_(t,e)],t=>t.findIndex(t=>t.id===e.id),r(t.options,"debugColumns","getIndex")),e.getIsFirstColumn=l=>{var n;return(null==(n=_(t,l)[0])?void 0:n.id)===e.id},e.getIsLastColumn=l=>{var n;let o=_(t,l);return(null==(n=o[o.length-1])?void 0:n.id)===e.id}},createTable:e=>{e.setColumnOrder=t=>null==e.options.onColumnOrderChange?void 0:e.options.onColumnOrderChange(t),e.resetColumnOrder=t=>{var l;e.setColumnOrder(t?[]:null!=(l=e.initialState.columnOrder)?l:[])},e._getOrderColumnsFn=i(()=>[e.getState().columnOrder,e.getState().grouping,e.options.groupedColumnMode],(e,t,l)=>n=>{let o=[];if(null!=e&&e.length){let t=[...e],l=[...n];for(;l.length&&t.length;){let e=t.shift(),n=l.findIndex(t=>t.id===e);n>-1&&o.push(l.splice(n,1)[0])}o=[...o,...l]}else o=n;var i=o;if(!(null!=t&&t.length)||!l)return i;let r=i.filter(e=>!t.includes(e.id));return"remove"===l?r:[...t.map(e=>i.find(t=>t.id===e)).filter(Boolean),...r]},r(e.options,"debugTable","_getOrderColumnsFn"))}},{getInitialState:e=>({columnPinning:M(),...e}),getDefaultOptions:e=>({onColumnPinningChange:n("columnPinning",e)}),createColumn:(e,t)=>{e.pin=l=>{let n=e.getLeafColumns().map(e=>e.id).filter(Boolean);t.setColumnPinning(e=>{var t,o,i,r,a,u;return"right"===l?{left:(null!=(i=null==e?void 0:e.left)?i:[]).filter(e=>!(null!=n&&n.includes(e))),right:[...(null!=(r=null==e?void 0:e.right)?r:[]).filter(e=>!(null!=n&&n.includes(e))),...n]}:"left"===l?{left:[...(null!=(a=null==e?void 0:e.left)?a:[]).filter(e=>!(null!=n&&n.includes(e))),...n],right:(null!=(u=null==e?void 0:e.right)?u:[]).filter(e=>!(null!=n&&n.includes(e)))}:{left:(null!=(t=null==e?void 0:e.left)?t:[]).filter(e=>!(null!=n&&n.includes(e))),right:(null!=(o=null==e?void 0:e.right)?o:[]).filter(e=>!(null!=n&&n.includes(e)))}})},e.getCanPin=()=>e.getLeafColumns().some(e=>{var l,n,o;return(null==(l=e.columnDef.enablePinning)||l)&&(null==(n=null!=(o=t.options.enableColumnPinning)?o:t.options.enablePinning)||n)}),e.getIsPinned=()=>{let l=e.getLeafColumns().map(e=>e.id),{left:n,right:o}=t.getState().columnPinning,i=l.some(e=>null==n?void 0:n.includes(e)),r=l.some(e=>null==o?void 0:o.includes(e));return i?"left":!!r&&"right"},e.getPinnedIndex=()=>{var l,n;let o=e.getIsPinned();return o?null!=(l=null==(n=t.getState().columnPinning)||null==(n=n[o])?void 0:n.indexOf(e.id))?l:-1:0}},createRow:(e,t)=>{e.getCenterVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.left,t.getState().columnPinning.right],(e,t,l)=>{let n=[...null!=t?t:[],...null!=l?l:[]];return e.filter(e=>!n.includes(e.column.id))},r(t.options,"debugRows","getCenterVisibleCells")),e.getLeftVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.left],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.column.id===t)).filter(Boolean).map(e=>({...e,position:"left"})),r(t.options,"debugRows","getLeftVisibleCells")),e.getRightVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.right],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.column.id===t)).filter(Boolean).map(e=>({...e,position:"right"})),r(t.options,"debugRows","getRightVisibleCells"))},createTable:e=>{e.setColumnPinning=t=>null==e.options.onColumnPinningChange?void 0:e.options.onColumnPinningChange(t),e.resetColumnPinning=t=>{var l,n;return e.setColumnPinning(t?M():null!=(l=null==(n=e.initialState)?void 0:n.columnPinning)?l:M())},e.getIsSomeColumnsPinned=t=>{var l,n,o;let i=e.getState().columnPinning;return t?!!(null==(l=i[t])?void 0:l.length):!!((null==(n=i.left)?void 0:n.length)||(null==(o=i.right)?void 0:o.length))},e.getLeftLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.left],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.id===t)).filter(Boolean),r(e.options,"debugColumns","getLeftLeafColumns")),e.getRightLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.right],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.id===t)).filter(Boolean),r(e.options,"debugColumns","getRightLeafColumns")),e.getCenterLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(e,t,l)=>{let n=[...null!=t?t:[],...null!=l?l:[]];return e.filter(e=>!n.includes(e.id))},r(e.options,"debugColumns","getCenterLeafColumns"))}},{createColumn:(e,t)=>{e._getFacetedRowModel=t.options.getFacetedRowModel&&t.options.getFacetedRowModel(t,e.id),e.getFacetedRowModel=()=>e._getFacetedRowModel?e._getFacetedRowModel():t.getPreFilteredRowModel(),e._getFacetedUniqueValues=t.options.getFacetedUniqueValues&&t.options.getFacetedUniqueValues(t,e.id),e.getFacetedUniqueValues=()=>e._getFacetedUniqueValues?e._getFacetedUniqueValues():new Map,e._getFacetedMinMaxValues=t.options.getFacetedMinMaxValues&&t.options.getFacetedMinMaxValues(t,e.id),e.getFacetedMinMaxValues=()=>{if(e._getFacetedMinMaxValues)return e._getFacetedMinMaxValues()}}},{getDefaultColumnDef:()=>({filterFn:"auto"}),getInitialState:e=>({columnFilters:[],...e}),getDefaultOptions:e=>({onColumnFiltersChange:n("columnFilters",e),filterFromLeafRows:!1,maxLeafRowFilterDepth:100}),createColumn:(e,t)=>{e.getAutoFilterFn=()=>{let l=t.getCoreRowModel().flatRows[0],n=null==l?void 0:l.getValue(e.id);return"string"==typeof n?v.includesString:"number"==typeof n?v.inNumberRange:"boolean"==typeof n||null!==n&&"object"==typeof n?v.equals:Array.isArray(n)?v.arrIncludes:v.weakEquals},e.getFilterFn=()=>{var l,n;return o(e.columnDef.filterFn)?e.columnDef.filterFn:"auto"===e.columnDef.filterFn?e.getAutoFilterFn():null!=(l=null==(n=t.options.filterFns)?void 0:n[e.columnDef.filterFn])?l:v[e.columnDef.filterFn]},e.getCanFilter=()=>{var l,n,o;return(null==(l=e.columnDef.enableColumnFilter)||l)&&(null==(n=t.options.enableColumnFilters)||n)&&(null==(o=t.options.enableFilters)||o)&&!!e.accessorFn},e.getIsFiltered=()=>e.getFilterIndex()>-1,e.getFilterValue=()=>{var l;return null==(l=t.getState().columnFilters)||null==(l=l.find(t=>t.id===e.id))?void 0:l.value},e.getFilterIndex=()=>{var l,n;return null!=(l=null==(n=t.getState().columnFilters)?void 0:n.findIndex(t=>t.id===e.id))?l:-1},e.setFilterValue=n=>{t.setColumnFilters(t=>{var o,i;let r=e.getFilterFn(),a=null==t?void 0:t.find(t=>t.id===e.id),u=l(n,a?a.value:void 0);if(b(r,u,e))return null!=(o=null==t?void 0:t.filter(t=>t.id!==e.id))?o:[];let g={id:e.id,value:u};return a?null!=(i=null==t?void 0:t.map(t=>t.id===e.id?g:t))?i:[]:null!=t&&t.length?[...t,g]:[g]})}},createRow:(e,t)=>{e.columnFilters={},e.columnFiltersMeta={}},createTable:e=>{e.setColumnFilters=t=>{let n=e.getAllLeafColumns();null==e.options.onColumnFiltersChange||e.options.onColumnFiltersChange(e=>{var o;return null==(o=l(t,e))?void 0:o.filter(e=>{let t=n.find(t=>t.id===e.id);return!(t&&b(t.getFilterFn(),e.value,t))&&!0})})},e.resetColumnFilters=t=>{var l,n;e.setColumnFilters(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.columnFilters)?l:[])},e.getPreFilteredRowModel=()=>e.getCoreRowModel(),e.getFilteredRowModel=()=>(!e._getFilteredRowModel&&e.options.getFilteredRowModel&&(e._getFilteredRowModel=e.options.getFilteredRowModel(e)),e.options.manualFiltering||!e._getFilteredRowModel)?e.getPreFilteredRowModel():e._getFilteredRowModel()}},{createTable:e=>{e._getGlobalFacetedRowModel=e.options.getFacetedRowModel&&e.options.getFacetedRowModel(e,"__global__"),e.getGlobalFacetedRowModel=()=>e.options.manualFiltering||!e._getGlobalFacetedRowModel?e.getPreFilteredRowModel():e._getGlobalFacetedRowModel(),e._getGlobalFacetedUniqueValues=e.options.getFacetedUniqueValues&&e.options.getFacetedUniqueValues(e,"__global__"),e.getGlobalFacetedUniqueValues=()=>e._getGlobalFacetedUniqueValues?e._getGlobalFacetedUniqueValues():new Map,e._getGlobalFacetedMinMaxValues=e.options.getFacetedMinMaxValues&&e.options.getFacetedMinMaxValues(e,"__global__"),e.getGlobalFacetedMinMaxValues=()=>{if(e._getGlobalFacetedMinMaxValues)return e._getGlobalFacetedMinMaxValues()}}},{getInitialState:e=>({globalFilter:void 0,...e}),getDefaultOptions:e=>({onGlobalFilterChange:n("globalFilter",e),globalFilterFn:"auto",getColumnCanGlobalFilter:t=>{var l;let n=null==(l=e.getCoreRowModel().flatRows[0])||null==(l=l._getAllCellsByColumnId()[t.id])?void 0:l.getValue();return"string"==typeof n||"number"==typeof n}}),createColumn:(e,t)=>{e.getCanGlobalFilter=()=>{var l,n,o,i;return(null==(l=e.columnDef.enableGlobalFilter)||l)&&(null==(n=t.options.enableGlobalFilter)||n)&&(null==(o=t.options.enableFilters)||o)&&(null==(i=null==t.options.getColumnCanGlobalFilter?void 0:t.options.getColumnCanGlobalFilter(e))||i)&&!!e.accessorFn}},createTable:e=>{e.getGlobalAutoFilterFn=()=>v.includesString,e.getGlobalFilterFn=()=>{var t,l;let{globalFilterFn:n}=e.options;return o(n)?n:"auto"===n?e.getGlobalAutoFilterFn():null!=(t=null==(l=e.options.filterFns)?void 0:l[n])?t:v[n]},e.setGlobalFilter=t=>{null==e.options.onGlobalFilterChange||e.options.onGlobalFilterChange(t)},e.resetGlobalFilter=t=>{e.setGlobalFilter(t?void 0:e.initialState.globalFilter)}}},{getInitialState:e=>({sorting:[],...e}),getDefaultColumnDef:()=>({sortingFn:"auto",sortUndefined:1}),getDefaultOptions:e=>({onSortingChange:n("sorting",e),isMultiSortEvent:e=>e.shiftKey}),createColumn:(e,t)=>{e.getAutoSortingFn=()=>{let l=t.getFilteredRowModel().flatRows.slice(10),n=!1;for(let t of l){let l=null==t?void 0:t.getValue(e.id);if("[object Date]"===Object.prototype.toString.call(l))return B.datetime;if("string"==typeof l&&(n=!0,l.split(D).length>1))return B.alphanumeric}return n?B.text:B.basic},e.getAutoSortDir=()=>{let l=t.getFilteredRowModel().flatRows[0];return"string"==typeof(null==l?void 0:l.getValue(e.id))?"asc":"desc"},e.getSortingFn=()=>{var l,n;if(!e)throw Error();return o(e.columnDef.sortingFn)?e.columnDef.sortingFn:"auto"===e.columnDef.sortingFn?e.getAutoSortingFn():null!=(l=null==(n=t.options.sortingFns)?void 0:n[e.columnDef.sortingFn])?l:B[e.columnDef.sortingFn]},e.toggleSorting=(l,n)=>{let o=e.getNextSortingOrder(),i=null!=l;t.setSorting(r=>{let a,u=null==r?void 0:r.find(t=>t.id===e.id),g=null==r?void 0:r.findIndex(t=>t.id===e.id),s=[],d=i?l:"desc"===o;if("toggle"!=(a=null!=r&&r.length&&e.getCanMultiSort()&&n?u?"toggle":"add":null!=r&&r.length&&g!==r.length-1?"replace":u?"toggle":"replace")||i||o||(a="remove"),"add"===a){var p;(s=[...r,{id:e.id,desc:d}]).splice(0,s.length-(null!=(p=t.options.maxMultiSortColCount)?p:Number.MAX_SAFE_INTEGER))}else s="toggle"===a?r.map(t=>t.id===e.id?{...t,desc:d}:t):"remove"===a?r.filter(t=>t.id!==e.id):[{id:e.id,desc:d}];return s})},e.getFirstSortDir=()=>{var l,n;return(null!=(l=null!=(n=e.columnDef.sortDescFirst)?n:t.options.sortDescFirst)?l:"desc"===e.getAutoSortDir())?"desc":"asc"},e.getNextSortingOrder=l=>{var n,o;let i=e.getFirstSortDir(),r=e.getIsSorted();return r?(r===i||null!=(n=t.options.enableSortingRemoval)&&!n||!!l&&null!=(o=t.options.enableMultiRemove)&&!o)&&("desc"===r?"asc":"desc"):i},e.getCanSort=()=>{var l,n;return(null==(l=e.columnDef.enableSorting)||l)&&(null==(n=t.options.enableSorting)||n)&&!!e.accessorFn},e.getCanMultiSort=()=>{var l,n;return null!=(l=null!=(n=e.columnDef.enableMultiSort)?n:t.options.enableMultiSort)?l:!!e.accessorFn},e.getIsSorted=()=>{var l;let n=null==(l=t.getState().sorting)?void 0:l.find(t=>t.id===e.id);return!!n&&(n.desc?"desc":"asc")},e.getSortIndex=()=>{var l,n;return null!=(l=null==(n=t.getState().sorting)?void 0:n.findIndex(t=>t.id===e.id))?l:-1},e.clearSorting=()=>{t.setSorting(t=>null!=t&&t.length?t.filter(t=>t.id!==e.id):[])},e.getToggleSortingHandler=()=>{let l=e.getCanSort();return n=>{l&&(null==n.persist||n.persist(),null==e.toggleSorting||e.toggleSorting(void 0,!!e.getCanMultiSort()&&(null==t.options.isMultiSortEvent?void 0:t.options.isMultiSortEvent(n))))}}},createTable:e=>{e.setSorting=t=>null==e.options.onSortingChange?void 0:e.options.onSortingChange(t),e.resetSorting=t=>{var l,n;e.setSorting(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.sorting)?l:[])},e.getPreSortedRowModel=()=>e.getGroupedRowModel(),e.getSortedRowModel=()=>(!e._getSortedRowModel&&e.options.getSortedRowModel&&(e._getSortedRowModel=e.options.getSortedRowModel(e)),e.options.manualSorting||!e._getSortedRowModel)?e.getPreSortedRowModel():e._getSortedRowModel()}},{getDefaultColumnDef:()=>({aggregatedCell:e=>{var t,l;return null!=(t=null==(l=e.getValue())||null==l.toString?void 0:l.toString())?t:null},aggregationFn:"auto"}),getInitialState:e=>({grouping:[],...e}),getDefaultOptions:e=>({onGroupingChange:n("grouping",e),groupedColumnMode:"reorder"}),createColumn:(e,t)=>{e.toggleGrouping=()=>{t.setGrouping(t=>null!=t&&t.includes(e.id)?t.filter(t=>t!==e.id):[...null!=t?t:[],e.id])},e.getCanGroup=()=>{var l,n;return(null==(l=e.columnDef.enableGrouping)||l)&&(null==(n=t.options.enableGrouping)||n)&&(!!e.accessorFn||!!e.columnDef.getGroupingValue)},e.getIsGrouped=()=>{var l;return null==(l=t.getState().grouping)?void 0:l.includes(e.id)},e.getGroupedIndex=()=>{var l;return null==(l=t.getState().grouping)?void 0:l.indexOf(e.id)},e.getToggleGroupingHandler=()=>{let t=e.getCanGroup();return()=>{t&&e.toggleGrouping()}},e.getAutoAggregationFn=()=>{let l=t.getCoreRowModel().flatRows[0],n=null==l?void 0:l.getValue(e.id);return"number"==typeof n?F.sum:"[object Date]"===Object.prototype.toString.call(n)?F.extent:void 0},e.getAggregationFn=()=>{var l,n;if(!e)throw Error();return o(e.columnDef.aggregationFn)?e.columnDef.aggregationFn:"auto"===e.columnDef.aggregationFn?e.getAutoAggregationFn():null!=(l=null==(n=t.options.aggregationFns)?void 0:n[e.columnDef.aggregationFn])?l:F[e.columnDef.aggregationFn]}},createTable:e=>{e.setGrouping=t=>null==e.options.onGroupingChange?void 0:e.options.onGroupingChange(t),e.resetGrouping=t=>{var l,n;e.setGrouping(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.grouping)?l:[])},e.getPreGroupedRowModel=()=>e.getFilteredRowModel(),e.getGroupedRowModel=()=>(!e._getGroupedRowModel&&e.options.getGroupedRowModel&&(e._getGroupedRowModel=e.options.getGroupedRowModel(e)),e.options.manualGrouping||!e._getGroupedRowModel)?e.getPreGroupedRowModel():e._getGroupedRowModel()},createRow:(e,t)=>{e.getIsGrouped=()=>!!e.groupingColumnId,e.getGroupingValue=l=>{if(e._groupingValuesCache.hasOwnProperty(l))return e._groupingValuesCache[l];let n=t.getColumn(l);return null!=n&&n.columnDef.getGroupingValue?(e._groupingValuesCache[l]=n.columnDef.getGroupingValue(e.original),e._groupingValuesCache[l]):e.getValue(l)},e._groupingValuesCache={}},createCell:(e,t,l,n)=>{e.getIsGrouped=()=>t.getIsGrouped()&&t.id===l.groupingColumnId,e.getIsPlaceholder=()=>!e.getIsGrouped()&&t.getIsGrouped(),e.getIsAggregated=()=>{var t;return!e.getIsGrouped()&&!e.getIsPlaceholder()&&!!(null!=(t=l.subRows)&&t.length)}}},{getInitialState:e=>({expanded:{},...e}),getDefaultOptions:e=>({onExpandedChange:n("expanded",e),paginateExpandedRows:!0}),createTable:e=>{let t=!1,l=!1;e._autoResetExpanded=()=>{var n,o;if(!t)return void e._queue(()=>{t=!0});if(null!=(n=null!=(o=e.options.autoResetAll)?o:e.options.autoResetExpanded)?n:!e.options.manualExpanding){if(l)return;l=!0,e._queue(()=>{e.resetExpanded(),l=!1})}},e.setExpanded=t=>null==e.options.onExpandedChange?void 0:e.options.onExpandedChange(t),e.toggleAllRowsExpanded=t=>{(null!=t?t:!e.getIsAllRowsExpanded())?e.setExpanded(!0):e.setExpanded({})},e.resetExpanded=t=>{var l,n;e.setExpanded(t?{}:null!=(l=null==(n=e.initialState)?void 0:n.expanded)?l:{})},e.getCanSomeRowsExpand=()=>e.getPrePaginationRowModel().flatRows.some(e=>e.getCanExpand()),e.getToggleAllRowsExpandedHandler=()=>t=>{null==t.persist||t.persist(),e.toggleAllRowsExpanded()},e.getIsSomeRowsExpanded=()=>{let t=e.getState().expanded;return!0===t||Object.values(t).some(Boolean)},e.getIsAllRowsExpanded=()=>{let t=e.getState().expanded;return"boolean"==typeof t?!0===t:!(!Object.keys(t).length||e.getRowModel().flatRows.some(e=>!e.getIsExpanded()))},e.getExpandedDepth=()=>{let t=0;return(!0===e.getState().expanded?Object.keys(e.getRowModel().rowsById):Object.keys(e.getState().expanded)).forEach(e=>{let l=e.split(".");t=Math.max(t,l.length)}),t},e.getPreExpandedRowModel=()=>e.getSortedRowModel(),e.getExpandedRowModel=()=>(!e._getExpandedRowModel&&e.options.getExpandedRowModel&&(e._getExpandedRowModel=e.options.getExpandedRowModel(e)),e.options.manualExpanding||!e._getExpandedRowModel)?e.getPreExpandedRowModel():e._getExpandedRowModel()},createRow:(e,t)=>{e.toggleExpanded=l=>{t.setExpanded(n=>{var o;let i=!0===n||!!(null!=n&&n[e.id]),r={};if(!0===n?Object.keys(t.getRowModel().rowsById).forEach(e=>{r[e]=!0}):r=n,l=null!=(o=l)?o:!i,!i&&l)return{...r,[e.id]:!0};if(i&&!l){let{[e.id]:t,...l}=r;return l}return n})},e.getIsExpanded=()=>{var l;let n=t.getState().expanded;return!!(null!=(l=null==t.options.getIsRowExpanded?void 0:t.options.getIsRowExpanded(e))?l:!0===n||(null==n?void 0:n[e.id]))},e.getCanExpand=()=>{var l,n,o;return null!=(l=null==t.options.getRowCanExpand?void 0:t.options.getRowCanExpand(e))?l:(null==(n=t.options.enableExpanding)||n)&&!!(null!=(o=e.subRows)&&o.length)},e.getIsAllParentsExpanded=()=>{let l=!0,n=e;for(;l&&n.parentId;)l=(n=t.getRow(n.parentId,!0)).getIsExpanded();return l},e.getToggleExpandedHandler=()=>{let t=e.getCanExpand();return()=>{t&&e.toggleExpanded()}}}},{getInitialState:e=>({...e,pagination:{...y(),...null==e?void 0:e.pagination}}),getDefaultOptions:e=>({onPaginationChange:n("pagination",e)}),createTable:e=>{let t=!1,n=!1;e._autoResetPageIndex=()=>{var l,o;if(!t)return void e._queue(()=>{t=!0});if(null!=(l=null!=(o=e.options.autoResetAll)?o:e.options.autoResetPageIndex)?l:!e.options.manualPagination){if(n)return;n=!0,e._queue(()=>{e.resetPageIndex(),n=!1})}},e.setPagination=t=>null==e.options.onPaginationChange?void 0:e.options.onPaginationChange(e=>l(t,e)),e.resetPagination=t=>{var l;e.setPagination(t?y():null!=(l=e.initialState.pagination)?l:y())},e.setPageIndex=t=>{e.setPagination(n=>{let o=l(t,n.pageIndex);return o=Math.max(0,Math.min(o,void 0===e.options.pageCount||-1===e.options.pageCount?Number.MAX_SAFE_INTEGER:e.options.pageCount-1)),{...n,pageIndex:o}})},e.resetPageIndex=t=>{var l,n;e.setPageIndex(t?0:null!=(l=null==(n=e.initialState)||null==(n=n.pagination)?void 0:n.pageIndex)?l:0)},e.resetPageSize=t=>{var l,n;e.setPageSize(t?10:null!=(l=null==(n=e.initialState)||null==(n=n.pagination)?void 0:n.pageSize)?l:10)},e.setPageSize=t=>{e.setPagination(e=>{let n=Math.max(1,l(t,e.pageSize)),o=Math.floor(e.pageSize*e.pageIndex/n);return{...e,pageIndex:o,pageSize:n}})},e.setPageCount=t=>e.setPagination(n=>{var o;let i=l(t,null!=(o=e.options.pageCount)?o:-1);return"number"==typeof i&&(i=Math.max(-1,i)),{...n,pageCount:i}}),e.getPageOptions=i(()=>[e.getPageCount()],e=>{let t=[];return e&&e>0&&(t=[...Array(e)].fill(null).map((e,t)=>t)),t},r(e.options,"debugTable","getPageOptions")),e.getCanPreviousPage=()=>e.getState().pagination.pageIndex>0,e.getCanNextPage=()=>{let{pageIndex:t}=e.getState().pagination,l=e.getPageCount();return -1===l||0!==l&&te.setPageIndex(e=>e-1),e.nextPage=()=>e.setPageIndex(e=>e+1),e.firstPage=()=>e.setPageIndex(0),e.lastPage=()=>e.setPageIndex(e.getPageCount()-1),e.getPrePaginationRowModel=()=>e.getExpandedRowModel(),e.getPaginationRowModel=()=>(!e._getPaginationRowModel&&e.options.getPaginationRowModel&&(e._getPaginationRowModel=e.options.getPaginationRowModel(e)),e.options.manualPagination||!e._getPaginationRowModel)?e.getPrePaginationRowModel():e._getPaginationRowModel(),e.getPageCount=()=>{var t;return null!=(t=e.options.pageCount)?t:Math.ceil(e.getRowCount()/e.getState().pagination.pageSize)},e.getRowCount=()=>{var t;return null!=(t=e.options.rowCount)?t:e.getPrePaginationRowModel().rows.length}}},{getInitialState:e=>({rowPinning:E(),...e}),getDefaultOptions:e=>({onRowPinningChange:n("rowPinning",e)}),createRow:(e,t)=>{e.pin=(l,n,o)=>{let i=n?e.getLeafRows().map(e=>{let{id:t}=e;return t}):[],r=new Set([...o?e.getParentRows().map(e=>{let{id:t}=e;return t}):[],e.id,...i]);t.setRowPinning(e=>{var t,n,o,i,a,u;return"bottom"===l?{top:(null!=(o=null==e?void 0:e.top)?o:[]).filter(e=>!(null!=r&&r.has(e))),bottom:[...(null!=(i=null==e?void 0:e.bottom)?i:[]).filter(e=>!(null!=r&&r.has(e))),...Array.from(r)]}:"top"===l?{top:[...(null!=(a=null==e?void 0:e.top)?a:[]).filter(e=>!(null!=r&&r.has(e))),...Array.from(r)],bottom:(null!=(u=null==e?void 0:e.bottom)?u:[]).filter(e=>!(null!=r&&r.has(e)))}:{top:(null!=(t=null==e?void 0:e.top)?t:[]).filter(e=>!(null!=r&&r.has(e))),bottom:(null!=(n=null==e?void 0:e.bottom)?n:[]).filter(e=>!(null!=r&&r.has(e)))}})},e.getCanPin=()=>{var l;let{enableRowPinning:n,enablePinning:o}=t.options;return"function"==typeof n?n(e):null==(l=null!=n?n:o)||l},e.getIsPinned=()=>{let l=[e.id],{top:n,bottom:o}=t.getState().rowPinning,i=l.some(e=>null==n?void 0:n.includes(e)),r=l.some(e=>null==o?void 0:o.includes(e));return i?"top":!!r&&"bottom"},e.getPinnedIndex=()=>{var l,n;let o=e.getIsPinned();if(!o)return -1;let i=null==(l="top"===o?t.getTopRows():t.getBottomRows())?void 0:l.map(e=>{let{id:t}=e;return t});return null!=(n=null==i?void 0:i.indexOf(e.id))?n:-1}},createTable:e=>{e.setRowPinning=t=>null==e.options.onRowPinningChange?void 0:e.options.onRowPinningChange(t),e.resetRowPinning=t=>{var l,n;return e.setRowPinning(t?E():null!=(l=null==(n=e.initialState)?void 0:n.rowPinning)?l:E())},e.getIsSomeRowsPinned=t=>{var l,n,o;let i=e.getState().rowPinning;return t?!!(null==(l=i[t])?void 0:l.length):!!((null==(n=i.top)?void 0:n.length)||(null==(o=i.bottom)?void 0:o.length))},e._getPinnedRows=(t,l,n)=>{var o;return(null==(o=e.options.keepPinnedRows)||o?(null!=l?l:[]).map(t=>{let l=e.getRow(t,!0);return l.getIsAllParentsExpanded()?l:null}):(null!=l?l:[]).map(e=>t.find(t=>t.id===e))).filter(Boolean).map(e=>({...e,position:n}))},e.getTopRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.top],(t,l)=>e._getPinnedRows(t,l,"top"),r(e.options,"debugRows","getTopRows")),e.getBottomRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.bottom],(t,l)=>e._getPinnedRows(t,l,"bottom"),r(e.options,"debugRows","getBottomRows")),e.getCenterRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.top,e.getState().rowPinning.bottom],(e,t,l)=>{let n=new Set([...null!=t?t:[],...null!=l?l:[]]);return e.filter(e=>!n.has(e.id))},r(e.options,"debugRows","getCenterRows"))}},{getInitialState:e=>({rowSelection:{},...e}),getDefaultOptions:e=>({onRowSelectionChange:n("rowSelection",e),enableRowSelection:!0,enableMultiRowSelection:!0,enableSubRowSelection:!0}),createTable:e=>{e.setRowSelection=t=>null==e.options.onRowSelectionChange?void 0:e.options.onRowSelectionChange(t),e.resetRowSelection=t=>{var l;return e.setRowSelection(t?{}:null!=(l=e.initialState.rowSelection)?l:{})},e.toggleAllRowsSelected=t=>{e.setRowSelection(l=>{t=void 0!==t?t:!e.getIsAllRowsSelected();let n={...l},o=e.getPreGroupedRowModel().flatRows;return t?o.forEach(e=>{e.getCanSelect()&&(n[e.id]=!0)}):o.forEach(e=>{delete n[e.id]}),n})},e.toggleAllPageRowsSelected=t=>e.setRowSelection(l=>{let n=void 0!==t?t:!e.getIsAllPageRowsSelected(),o={...l};return e.getRowModel().rows.forEach(t=>{G(o,t.id,n,!0,e)}),o}),e.getPreSelectedRowModel=()=>e.getCoreRowModel(),e.getSelectedRowModel=i(()=>[e.getState().rowSelection,e.getCoreRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getSelectedRowModel")),e.getFilteredSelectedRowModel=i(()=>[e.getState().rowSelection,e.getFilteredRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getFilteredSelectedRowModel")),e.getGroupedSelectedRowModel=i(()=>[e.getState().rowSelection,e.getSortedRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getGroupedSelectedRowModel")),e.getIsAllRowsSelected=()=>{let t=e.getFilteredRowModel().flatRows,{rowSelection:l}=e.getState(),n=!!(t.length&&Object.keys(l).length);return n&&t.some(e=>e.getCanSelect()&&!l[e.id])&&(n=!1),n},e.getIsAllPageRowsSelected=()=>{let t=e.getPaginationRowModel().flatRows.filter(e=>e.getCanSelect()),{rowSelection:l}=e.getState(),n=!!t.length;return n&&t.some(e=>!l[e.id])&&(n=!1),n},e.getIsSomeRowsSelected=()=>{var t;let l=Object.keys(null!=(t=e.getState().rowSelection)?t:{}).length;return l>0&&l{let t=e.getPaginationRowModel().flatRows;return!e.getIsAllPageRowsSelected()&&t.filter(e=>e.getCanSelect()).some(e=>e.getIsSelected()||e.getIsSomeSelected())},e.getToggleAllRowsSelectedHandler=()=>t=>{e.toggleAllRowsSelected(t.target.checked)},e.getToggleAllPageRowsSelectedHandler=()=>t=>{e.toggleAllPageRowsSelected(t.target.checked)}},createRow:(e,t)=>{e.toggleSelected=(l,n)=>{let o=e.getIsSelected();t.setRowSelection(i=>{var r;if(l=void 0!==l?l:!o,e.getCanSelect()&&o===l)return i;let a={...i};return G(a,e.id,l,null==(r=null==n?void 0:n.selectChildren)||r,t),a})},e.getIsSelected=()=>{let{rowSelection:l}=t.getState();return A(e,l)},e.getIsSomeSelected=()=>{let{rowSelection:l}=t.getState();return"some"===H(e,l)},e.getIsAllSubRowsSelected=()=>{let{rowSelection:l}=t.getState();return"all"===H(e,l)},e.getCanSelect=()=>{var l;return"function"==typeof t.options.enableRowSelection?t.options.enableRowSelection(e):null==(l=t.options.enableRowSelection)||l},e.getCanSelectSubRows=()=>{var l;return"function"==typeof t.options.enableSubRowSelection?t.options.enableSubRowSelection(e):null==(l=t.options.enableSubRowSelection)||l},e.getCanMultiSelect=()=>{var l;return"function"==typeof t.options.enableMultiRowSelection?t.options.enableMultiRowSelection(e):null==(l=t.options.enableMultiRowSelection)||l},e.getToggleSelectedHandler=()=>{let t=e.getCanSelect();return l=>{var n;t&&e.toggleSelected(null==(n=l.target)?void 0:n.checked)}}}},{getDefaultColumnDef:()=>V,getInitialState:e=>({columnSizing:{},columnSizingInfo:P(),...e}),getDefaultOptions:e=>({columnResizeMode:"onEnd",columnResizeDirection:"ltr",onColumnSizingChange:n("columnSizing",e),onColumnSizingInfoChange:n("columnSizingInfo",e)}),createColumn:(e,t)=>{e.getSize=()=>{var l,n,o;let i=t.getState().columnSizing[e.id];return Math.min(Math.max(null!=(l=e.columnDef.minSize)?l:V.minSize,null!=(n=null!=i?i:e.columnDef.size)?n:V.size),null!=(o=e.columnDef.maxSize)?o:V.maxSize)},e.getStart=i(e=>[e,_(t,e),t.getState().columnSizing],(t,l)=>l.slice(0,e.getIndex(t)).reduce((e,t)=>e+t.getSize(),0),r(t.options,"debugColumns","getStart")),e.getAfter=i(e=>[e,_(t,e),t.getState().columnSizing],(t,l)=>l.slice(e.getIndex(t)+1).reduce((e,t)=>e+t.getSize(),0),r(t.options,"debugColumns","getAfter")),e.resetSize=()=>{t.setColumnSizing(t=>{let{[e.id]:l,...n}=t;return n})},e.getCanResize=()=>{var l,n;return(null==(l=e.columnDef.enableResizing)||l)&&(null==(n=t.options.enableColumnResizing)||n)},e.getIsResizing=()=>t.getState().columnSizingInfo.isResizingColumn===e.id},createHeader:(e,t)=>{e.getSize=()=>{let t=0,l=e=>{if(e.subHeaders.length)e.subHeaders.forEach(l);else{var n;t+=null!=(n=e.column.getSize())?n:0}};return l(e),t},e.getStart=()=>{if(e.index>0){let t=e.headerGroup.headers[e.index-1];return t.getStart()+t.getSize()}return 0},e.getResizeHandler=l=>{let n=t.getColumn(e.column.id),o=null==n?void 0:n.getCanResize();return i=>{if(!n||!o||(null==i.persist||i.persist(),x(i)&&i.touches&&i.touches.length>1))return;let r=e.getSize(),a=e?e.getLeafHeaders().map(e=>[e.column.id,e.column.getSize()]):[[n.id,n.getSize()]],u=x(i)?Math.round(i.touches[0].clientX):i.clientX,g={},s=(e,l)=>{"number"==typeof l&&(t.setColumnSizingInfo(e=>{var n,o;let i="rtl"===t.options.columnResizeDirection?-1:1,r=(l-(null!=(n=null==e?void 0:e.startOffset)?n:0))*i,a=Math.max(r/(null!=(o=null==e?void 0:e.startSize)?o:0),-.999999);return e.columnSizingStart.forEach(e=>{let[t,l]=e;g[t]=Math.round(100*Math.max(l+l*a,0))/100}),{...e,deltaOffset:r,deltaPercentage:a}}),("onChange"===t.options.columnResizeMode||"end"===e)&&t.setColumnSizing(e=>({...e,...g})))},d=e=>{s("end",e),t.setColumnSizingInfo(e=>({...e,isResizingColumn:!1,startOffset:null,startSize:null,deltaOffset:null,deltaPercentage:null,columnSizingStart:[]}))},p=l||("u">typeof document?document:null),c={moveHandler:e=>s("move",e.clientX),upHandler:e=>{null==p||p.removeEventListener("mousemove",c.moveHandler),null==p||p.removeEventListener("mouseup",c.upHandler),d(e.clientX)}},f={moveHandler:e=>(e.cancelable&&(e.preventDefault(),e.stopPropagation()),s("move",e.touches[0].clientX),!1),upHandler:e=>{var t;null==p||p.removeEventListener("touchmove",f.moveHandler),null==p||p.removeEventListener("touchend",f.upHandler),e.cancelable&&(e.preventDefault(),e.stopPropagation()),d(null==(t=e.touches[0])?void 0:t.clientX)}},m=!!function(){if("boolean"==typeof I)return I;let e=!1;try{let t=()=>{};window.addEventListener("test",t,{get passive(){return e=!0,!1}}),window.removeEventListener("test",t)}catch(t){e=!1}return I=e}()&&{passive:!1};x(i)?(null==p||p.addEventListener("touchmove",f.moveHandler,m),null==p||p.addEventListener("touchend",f.upHandler,m)):(null==p||p.addEventListener("mousemove",c.moveHandler,m),null==p||p.addEventListener("mouseup",c.upHandler,m)),t.setColumnSizingInfo(e=>({...e,startOffset:u,startSize:r,deltaOffset:0,deltaPercentage:0,columnSizingStart:a,isResizingColumn:n.id}))}}},createTable:e=>{e.setColumnSizing=t=>null==e.options.onColumnSizingChange?void 0:e.options.onColumnSizingChange(t),e.setColumnSizingInfo=t=>null==e.options.onColumnSizingInfoChange?void 0:e.options.onColumnSizingInfoChange(t),e.resetColumnSizing=t=>{var l;e.setColumnSizing(t?{}:null!=(l=e.initialState.columnSizing)?l:{})},e.resetHeaderSizeInfo=t=>{var l;e.setColumnSizingInfo(t?P():null!=(l=e.initialState.columnSizingInfo)?l:P())},e.getTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getLeftTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getLeftHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getCenterTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getCenterHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getRightTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getRightHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0}}}];function k(e){var t,n;let o=[...q,...null!=(t=e._features)?t:[]],a={_features:o},u=a._features.reduce((e,t)=>Object.assign(e,null==t.getDefaultOptions?void 0:t.getDefaultOptions(a)),{}),g={...null!=(n=e.initialState)?n:{}};a._features.forEach(e=>{var t;g=null!=(t=null==e.getInitialState?void 0:e.getInitialState(g))?t:g});let s=[],d=!1,p={_features:o,options:{...u,...e},initialState:g,_queue:e=>{s.push(e),d||(d=!0,Promise.resolve().then(()=>{for(;s.length;)s.shift()();d=!1}).catch(e=>setTimeout(()=>{throw e})))},reset:()=>{a.setState(a.initialState)},setOptions:e=>{var t;t=l(e,a.options),a.options=a.options.mergeOptions?a.options.mergeOptions(u,t):{...u,...t}},getState:()=>a.options.state,setState:e=>{null==a.options.onStateChange||a.options.onStateChange(e)},_getRowId:(e,t,l)=>{var n;return null!=(n=null==a.options.getRowId?void 0:a.options.getRowId(e,t,l))?n:`${l?[l.id,t].join("."):t}`},getCoreRowModel:()=>(a._getCoreRowModel||(a._getCoreRowModel=a.options.getCoreRowModel(a)),a._getCoreRowModel()),getRowModel:()=>a.getPaginationRowModel(),getRow:(e,t)=>{let l=(t?a.getPrePaginationRowModel():a.getRowModel()).rowsById[e];if(!l&&!(l=a.getCoreRowModel().rowsById[e]))throw Error();return l},_getDefaultColumnDef:i(()=>[a.options.defaultColumn],e=>{var t;return e=null!=(t=e)?t:{},{header:e=>{let t=e.header.column.columnDef;return t.accessorKey?t.accessorKey:t.accessorFn?t.id:null},cell:e=>{var t,l;return null!=(t=null==(l=e.renderValue())||null==l.toString?void 0:l.toString())?t:null},...a._features.reduce((e,t)=>Object.assign(e,null==t.getDefaultColumnDef?void 0:t.getDefaultColumnDef()),{}),...e}},r(e,"debugColumns","_getDefaultColumnDef")),_getColumnDefs:()=>a.options.columns,getAllColumns:i(()=>[a._getColumnDefs()],e=>{let t=function(e,l,n){return void 0===n&&(n=0),e.map(e=>{let o=function(e,t,l,n){var o,a;let u,g={...e._getDefaultColumnDef(),...t},s=g.accessorKey,d=null!=(o=null!=(a=g.id)?a:s?"function"==typeof String.prototype.replaceAll?s.replaceAll(".","_"):s.replace(/\./g,"_"):void 0)?o:"string"==typeof g.header?g.header:void 0;if(g.accessorFn?u=g.accessorFn:s&&(u=s.includes(".")?e=>{let t=e;for(let e of s.split(".")){var l;t=null==(l=t)?void 0:l[e]}return t}:e=>e[g.accessorKey]),!d)throw Error();let p={id:`${String(d)}`,accessorFn:u,parent:n,depth:l,columnDef:g,columns:[],getFlatColumns:i(()=>[!0],()=>{var e;return[p,...null==(e=p.columns)?void 0:e.flatMap(e=>e.getFlatColumns())]},r(e.options,"debugColumns","column.getFlatColumns")),getLeafColumns:i(()=>[e._getOrderColumnsFn()],e=>{var t;return null!=(t=p.columns)&&t.length?e(p.columns.flatMap(e=>e.getLeafColumns())):[p]},r(e.options,"debugColumns","column.getLeafColumns"))};for(let t of e._features)null==t.createColumn||t.createColumn(p,e);return p}(a,e,n,l);return o.columns=e.columns?t(e.columns,o,n+1):[],o})};return t(e)},r(e,"debugColumns","getAllColumns")),getAllFlatColumns:i(()=>[a.getAllColumns()],e=>e.flatMap(e=>e.getFlatColumns()),r(e,"debugColumns","getAllFlatColumns")),_getAllFlatColumnsById:i(()=>[a.getAllFlatColumns()],e=>e.reduce((e,t)=>(e[t.id]=t,e),{}),r(e,"debugColumns","getAllFlatColumnsById")),getAllLeafColumns:i(()=>[a.getAllColumns(),a._getOrderColumnsFn()],(e,t)=>t(e.flatMap(e=>e.getLeafColumns())),r(e,"debugColumns","getAllLeafColumns")),getColumn:e=>a._getAllFlatColumnsById()[e]};Object.assign(a,p);for(let e=0;e{var n;t.push(e),null!=(n=e.subRows)&&n.length&&e.getIsExpanded()&&e.subRows.forEach(l)};return e.rows.forEach(l),{rows:t,flatRows:e.flatRows,rowsById:e.rowsById}}e.s(["createTable",0,k,"getCoreRowModel",0,function(){return e=>i(()=>[e.options.data],t=>{let l={rows:[],flatRows:[],rowsById:{}},n=function(t,o,i){void 0===o&&(o=0);let r=[];for(let u=0;ue._autoResetPageIndex()))},"getExpandedRowModel",0,function(){return e=>i(()=>[e.getState().expanded,e.getPreExpandedRowModel(),e.options.paginateExpandedRows],(e,t,l)=>t.rows.length&&(!0===e||Object.keys(null!=e?e:{}).length)&&l?j(t):t,r(e.options,"debugTable","getExpandedRowModel"))},"getFilteredRowModel",0,function(){return e=>i(()=>[e.getPreFilteredRowModel(),e.getState().columnFilters,e.getState().globalFilter],(t,l,n)=>{var o,i,r,a,u,g,d,p,c,f;let m,C,w,R,h,v,S,b,F,M;if(!t.rows.length||!(null!=l&&l.length)&&!n){for(let e=0;e{var l;let n=e.getColumn(t.id);if(!n)return;let o=n.getFilterFn();o&&V.push({id:t.id,filterFn:o,resolvedValue:null!=(l=null==o.resolveFilterValue?void 0:o.resolveFilterValue(t.value))?l:t.value})});let I=(null!=l?l:[]).map(e=>e.id),x=e.getGlobalFilterFn(),_=e.getAllLeafColumns().filter(e=>e.getCanGlobalFilter());n&&x&&_.length&&(I.push("__global__"),_.forEach(e=>{var t;P.push({id:e.id,filterFn:x,resolvedValue:null!=(t=null==x.resolveFilterValue?void 0:x.resolveFilterValue(n))?t:n})}));for(let e=0;e{l.columnFiltersMeta[t]=e})}if(P.length){for(let e=0;e{l.columnFiltersMeta[t]=e})){l.columnFilters.__global__=!0;break}}!0!==l.columnFilters.__global__&&(l.columnFilters.__global__=!1)}}return o=t.rows,i=e=>{for(let t=0;te._autoResetPageIndex()))},"getPaginationRowModel",0,function(e){return e=>i(()=>[e.getState().pagination,e.getPrePaginationRowModel(),e.options.paginateExpandedRows?void 0:e.getState().expanded],(t,l)=>{let n;if(!l.rows.length)return l;let{pageSize:o,pageIndex:i}=t,{rows:r,flatRows:a,rowsById:u}=l,g=o*i;r=r.slice(g,g+o),(n=e.options.paginateExpandedRows?{rows:r,flatRows:a,rowsById:u}:j({rows:r,flatRows:a,rowsById:u})).flatRows=[];let s=e=>{n.flatRows.push(e),e.subRows.length&&e.subRows.forEach(s)};return n.rows.forEach(s),n},r(e.options,"debugTable","getPaginationRowModel"))},"getSortedRowModel",0,function(){return e=>i(()=>[e.getState().sorting,e.getPreSortedRowModel()],(t,l)=>{if(!l.rows.length||!(null!=t&&t.length))return l;let n=e.getState().sorting,o=[],i=n.filter(t=>{var l;return null==(l=e.getColumn(t.id))?void 0:l.getCanSort()}),r={};i.forEach(t=>{let l=e.getColumn(t.id);l&&(r[t.id]={sortUndefined:l.columnDef.sortUndefined,invertSorting:l.columnDef.invertSorting,sortingFn:l.getSortingFn()})});let a=e=>{let t=e.map(e=>({...e}));return t.sort((e,t)=>{for(let n=0;n{var t;o.push(e),null!=(t=e.subRows)&&t.length&&(e.subRows=a(e.subRows))}),t};return{rows:a(l.rows),flatRows:o,rowsById:l.rowsById}},r(e.options,"debugTable","getSortedRowModel",()=>e._autoResetPageIndex()))}],682830),e.s(["flexRender",0,function(e,l){var n,o,i;let r;return e?"function"==typeof(o=n=e)&&(r=Object.getPrototypeOf(o)).prototype&&r.prototype.isReactComponent||"function"==typeof n||"object"==typeof(i=n)&&"symbol"==typeof i.$$typeof&&["react.memo","react.forward_ref"].includes(i.$$typeof.description)?t.createElement(e,l):e:null},"useReactTable",0,function(e){let l={state:{},onStateChange:()=>{},renderFallbackValue:null,...e},[n]=t.useState(()=>({current:k(l)})),[o,i]=t.useState(()=>n.current.initialState);return n.current.setOptions(t=>({...t,...e,state:{...o,...e.state},onStateChange:t=>{i(t),null==e.onStateChange||e.onStateChange(t)}})),n.current}],152990)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/024dtgrf2jszs.js b/litellm/proxy/_experimental/out/_next/static/chunks/024dtgrf2jszs.js deleted file mode 100644 index b1a91138cd5..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/024dtgrf2jszs.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,510674,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(431703),r=e.i(135214),i=e.i(708347);let n=(0,s.createQueryKeys)("projects"),o=[...i.all_admin_roles,...i.internalUserRoles],d=async e=>{let t=(0,l.getProxyBaseUrl)(),s=`${t}/project/list`,r=await fetch(s,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,a.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return r.json()};e.s(["projectKeys",0,n,"useProjects",0,()=>{let{accessToken:e,userRole:s}=(0,r.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>d(e),enabled:!!e&&o.includes(s)})}])},207082,e=>{"use strict";var t=e.i(619273),s=e.i(266027),l=e.i(243652),a=e.i(602869),r=e.i(431703),i=e.i(135214);let n=(0,l.createQueryKeys)("keys"),o=async(e,t,s,l={})=>{try{let i=(0,a.getProxyBaseUrl)(),n=new URLSearchParams(Object.entries({team_id:l.teamID,project_id:l.projectID,agent_id:l.agentID,organization_id:l.organizationID,key_alias:l.selectedKeyAlias,key_hash:l.keyHash,user_id:l.userID,page:t,size:s,sort_by:l.sortBy,sort_order:l.sortOrder,expand:l.expand,status:l.status,return_full_object:"true",include_team_keys:"true",include_created_by_keys:"true",substring_matching:"true"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),o=`${i?`${i}/key/list`:"/key/list"}?${n}`,d=await fetch(o,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!d.ok){let e=await d.json(),t=(0,r.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return await d.json()}catch(e){throw console.error("Failed to list keys:",e),e}},d=(0,l.createQueryKeys)("deletedKeys");e.s(["keyKeys",0,n,"useDeletedKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:d.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,{...a,status:"deleted"}),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})},"useKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:n.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,a),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})}])},109034,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(135214);let r=(0,s.createQueryKeys)("tags");e.s(["useTags",0,()=>{let{accessToken:e,userId:s,userRole:i}=(0,a.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,l.tagListCall)(e),enabled:!!(e&&s&&i)})}])},552130,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select agents",disabled:d=!1})=>{let[c,u]=(0,s.useState)([]),[m,p]=(0,s.useState)([]),[g,h]=(0,s.useState)(!1);(0,s.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,a.getAgentsList)(n),t=e?.agents||[];u(t);let s=new Set;t.forEach(e=>{let t=e.agent_access_groups;t&&Array.isArray(t)&&t.forEach(e=>s.add(e))}),p(Array.from(s))}catch(e){console.error("Error fetching agents:",e)}finally{h(!1)}}})()},[n]);let x=[...m.map(e=>({label:e,value:`group:${e}`,isAccessGroup:!0,searchText:`${e} Access Group`})),...c.map(e=>({label:`${e.agent_name||e.agent_id}`,value:e.agent_id,isAccessGroup:!1,searchText:`${e.agent_name||e.agent_id} ${e.agent_id} Agent`}))],y=[...r?.agents||[],...(r?.accessGroups||[]).map(e=>`group:${e}`)];return(0,t.jsx)("div",{children:(0,t.jsx)(l.Select,{mode:"multiple",placeholder:o,onChange:t=>{e({agents:t.filter(e=>!e.startsWith("group:")),accessGroups:t.filter(e=>e.startsWith("group:")).map(e=>e.replace("group:",""))})},value:y,loading:g,className:i,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:d,filterOption:(e,t)=>(x.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:x.map(e=>(0,t.jsx)(l.Select.Option,{value:e.value,label:e.label,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:e.isAccessGroup?"#52c41a":"#722ed1",flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:e.isAccessGroup?"#52c41a":"#722ed1",fontSize:"12px",fontWeight:500,opacity:.8},children:e.isAccessGroup?"Access Group":"Agent"})]})},e.value))})})}])},557662,e=>{"use strict";let t="/ui/assets/logos/",s=[{id:"arize",displayName:"Arize",logo:`${t}arize.png`,supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_id:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:`${t}braintrust.png`,supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:`${t}custom.svg`,supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"galileo",displayName:"Galileo",logo:`${t}galileo.ico`,supports_key_team_logging:!1,dynamic_params:{GALILEO_API_KEY:"password",GALILEO_PROJECT_ID:"text",GALILEO_LOG_STREAM_ID:"text",GALILEO_BASE_URL:"text",GALILEO_USERNAME:"text",GALILEO_PASSWORD:"password"},description:"Galileo AI Observability Integration"},{id:"datadog",displayName:"Datadog",logo:`${t}datadog.png`,supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:`${t}lago.svg`,supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:`${t}langsmith.png`,supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:`${t}openmeter.png`,supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:`${t}otel.png`,supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"},{id:"SQS",displayName:"SQS",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{sqs_queue_url:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"SQS Queue (AWS) Logging Integration"}],l=s.reduce((e,t)=>(e[t.displayName]=t,e),{}),a=s.reduce((e,t)=>(e[t.displayName]=t.id,e),{}),r=s.reduce((e,t)=>(e[t.id]=t.displayName,e),{});e.s(["callbackInfo",0,l,"callback_map",0,a,"mapDisplayToInternalNames",0,e=>e.map(e=>a[e]||e),"mapInternalToDisplayNames",0,e=>e.map(e=>r[e]||e),"reverse_callback_map",0,r])},9314,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(981339),a=e.i(645526),r=e.i(599724),i=e.i(263147);e.s(["default",0,({value:e,onChange:n,placeholder:o="Select access groups",disabled:d=!1,style:c,className:u,showLabel:m=!1,labelText:p="Access Group",allowClear:g=!0})=>{let{data:h,isLoading:x,isError:y}=(0,i.useAccessGroups)();if(x)return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(l.Skeleton.Input,{active:!0,block:!0,style:{height:32,...c}})]});let f=(h??[]).map(e=>({label:(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"font-medium",children:e.access_group_name})," ",(0,t.jsxs)("span",{className:"text-gray-400 text-xs",children:["(",e.access_group_id,")"]})]}),value:e.access_group_id,selectedLabel:e.access_group_name,searchText:`${e.access_group_name} ${e.access_group_id}`}));return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(s.Select,{mode:"multiple",value:e,placeholder:o,onChange:n,disabled:d,allowClear:g,showSearch:!0,style:{width:"100%",...c},className:`rounded-md ${u??""}`,notFoundContent:y?(0,t.jsx)("span",{className:"text-red-500",children:"Failed to load access groups"}):"No access groups found",filterOption:(e,t)=>(f.find(e=>e.value===t?.value)?.searchText??"").toLowerCase().includes(e.toLowerCase()),optionLabelProp:"selectedLabel",options:f.map(e=>({label:e.label,value:e.value,selectedLabel:e.selectedLabel}))})]})}])},392110,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(592968),r=e.i(312361),i=e.i(790848),n=e.i(536916),o=e.i(827252),d=e.i(779241);let{Option:c}=l.Select;e.s(["default",0,({form:e,autoRotationEnabled:u,onAutoRotationChange:m,rotationInterval:p,onRotationIntervalChange:g,isCreateMode:h=!1,neverExpire:x=!1,onNeverExpireChange:y})=>{let f=p&&!["7d","30d","90d","180d","365d"].includes(p),[b,_]=(0,s.useState)(f),[j,v]=(0,s.useState)(f?p:""),[w,N]=(0,s.useState)(e?.getFieldValue?.("duration")||"");return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Key Expiry Settings"}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Expire Key"}),(0,t.jsx)(a.Tooltip,{title:"Set when this key should expire. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days). Leave empty to keep the current expiry unchanged.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),!h&&y&&(0,t.jsx)(n.Checkbox,{checked:x,onChange:t=>{let s=t.target.checked;y(s),s&&(N(""),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",""):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:""}))},className:"ml-2 text-sm font-normal text-gray-600",children:"Never Expire"})]}),(0,t.jsx)(d.TextInput,{name:"duration",placeholder:h?"e.g., 30d or leave empty to never expire":"e.g., 30d",className:"w-full",value:w,onValueChange:t=>{N(t),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",t):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:t})},disabled:!h&&x})]})]}),(0,t.jsx)(r.Divider,{}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Auto-Rotation Settings"}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Enable Auto-Rotation"}),(0,t.jsx)(a.Tooltip,{title:"Key will automatically regenerate at the specified interval for enhanced security.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsx)(i.Switch,{checked:u,onChange:m,size:"default",className:u?"":"bg-gray-400"})]}),u&&(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Rotation Interval"}),(0,t.jsx)(a.Tooltip,{title:"How often the key should be automatically rotated. Choose the interval that best fits your security requirements.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)(l.Select,{value:b?"custom":p,onChange:e=>{"custom"===e?_(!0):(_(!1),v(""),g(e))},className:"w-full",placeholder:"Select interval",children:[(0,t.jsx)(c,{value:"7d",children:"7 days"}),(0,t.jsx)(c,{value:"30d",children:"30 days"}),(0,t.jsx)(c,{value:"90d",children:"90 days"}),(0,t.jsx)(c,{value:"180d",children:"180 days"}),(0,t.jsx)(c,{value:"365d",children:"365 days"}),(0,t.jsx)(c,{value:"custom",children:"Custom interval"})]}),b&&(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(d.TextInput,{value:j,onChange:e=>{let t=e.target.value;v(t),g(t)},placeholder:"e.g., 1s, 5m, 2h, 14d"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Supported formats: seconds (s), minutes (m), hours (h), days (d)"})]})]})]})]}),u&&(0,t.jsx)("div",{className:"bg-blue-50 p-3 rounded-md text-sm text-blue-700",children:"When rotation occurs, you'll receive a notification with the new key. The old key will be deactivated after a brief grace period."})]})]})}])},533882,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(250980),a=e.i(797672),r=e.i(68155),i=e.i(304967),n=e.i(629569),o=e.i(599724),d=e.i(269200),c=e.i(427612),u=e.i(64848),m=e.i(942232),p=e.i(496020),g=e.i(977572),h=e.i(992619),x=e.i(727749);e.s(["default",0,({accessToken:e,initialModelAliases:y={},onAliasUpdate:f,showExampleConfig:b=!0})=>{let[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)({aliasName:"",targetModel:""}),[N,k]=(0,s.useState)(null);(0,s.useEffect)(()=>{j(Object.entries(y).map(([e,t],s)=>({id:`${s}-${e}`,aliasName:e,targetModel:t})))},[y]);let S=()=>{if(!N)return;if(!N.aliasName||!N.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.id!==N.id&&e.aliasName===N.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=_.map(e=>e.id===N.id?N:e);j(e),k(null);let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias updated successfully")},C=()=>{k(null)},T=_.reduce((e,t)=>(e[t.aliasName]=t.targetModel,e),{});return(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:v.aliasName,onChange:e=>w({...v,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model"}),(0,t.jsx)(h.default,{accessToken:e,value:v.targetModel,placeholder:"Select target model",onChange:e=>w({...v,targetModel:e}),showLabel:!1})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:()=>{if(!v.aliasName||!v.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.aliasName===v.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=[..._,{id:`${Date.now()}-${v.aliasName}`,aliasName:v.aliasName,targetModel:v.targetModel}];j(e),w({aliasName:"",targetModel:""});let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias added successfully")},disabled:!v.aliasName||!v.targetModel,className:`flex items-center px-4 py-2 rounded-md text-sm ${!v.aliasName||!v.targetModel?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(l.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(d.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(c.TableHead,{children:(0,t.jsxs)(p.TableRow,{children:[(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Target Model"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(m.TableBody,{children:[_.map(s=>(0,t.jsx)(p.TableRow,{className:"h-8",children:N&&N.id===s.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:N.aliasName,onChange:e=>k({...N,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)(h.default,{accessToken:e,value:N.targetModel,onChange:e=>k({...N,targetModel:e}),showLabel:!1,style:{height:"32px"}})}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:S,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:C,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-900",children:s.aliasName}),(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-500",children:s.targetModel}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{k({...s})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(a.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>{var e;let t,l;return e=s.id,j(t=_.filter(t=>t.id!==e)),l={},void(t.forEach(e=>{l[e.aliasName]=e.targetModel}),f&&f(l),x.default.success("Alias deleted successfully"))},className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(r.TrashIcon,{className:"w-3 h-3"})})]})})]})},s.id)),0===_.length&&(0,t.jsx)(p.TableRow,{children:(0,t.jsx)(g.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),b&&(0,t.jsxs)(i.Card,{children:[(0,t.jsx)(n.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(o.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["model_aliases:",0===Object.keys(T).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"  # No aliases configured yet"]}):Object.entries(T).map(([e,s])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'  "',e,'": "',s,'"']},e))]})})]})]})}])},844565,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select pass through routes",disabled:d=!1,teamId:c})=>{let[u,m]=(0,s.useState)([]),[p,g]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.getPassThroughEndpointsCall)(n,c);if(e.endpoints){let t=e.endpoints.flatMap(e=>{let t=e.path,s=e.methods;return s&&s.length>0?s.map(e=>({label:`${e} ${t}`,value:t})):[{label:t,value:t}]});m(t)}}catch(e){console.error("Error fetching pass through routes:",e)}finally{g(!1)}}})()},[n,c]),(0,t.jsx)(l.Select,{mode:"tags",placeholder:o,onChange:e,value:r,loading:p,className:i,allowClear:!0,options:u,optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})}])},810757,477386,e=>{"use strict";var t=e.i(271645);let s=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}))});e.s(["CogIcon",0,s],810757);let l=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"}))});e.s(["BanIcon",0,l],477386)},266484,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(592968),a=e.i(312361),r=e.i(827252),i=e.i(994388),n=e.i(304967),o=e.i(779241),d=e.i(988297),c=e.i(68155),u=e.i(810757),m=e.i(477386),p=e.i(557662),g=e.i(555987),h=e.i(435451);let{Option:x}=s.Select;e.s(["default",0,({value:e=[],onChange:y,disabledCallbacks:f=[],onDisabledCallbacksChange:b})=>{let _=Object.entries(p.callbackInfo).filter(([e,t])=>t.supports_key_team_logging).map(([e,t])=>e),j=Object.keys(p.callbackInfo),v=e=>{y?.(e)},w=(t,s,l)=>{let a=[...e];if("callback_name"===s){let e=p.callback_map[l]||l;a[t]={...a[t],[s]:e,callback_vars:{}}}else a[t]={...a[t],[s]:l};v(a)},N=(t,s,l)=>{let a=[...e];a[t]={...a[t],callback_vars:{...a[t].callback_vars,[s]:l}},v(a)};return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(m.BanIcon,{className:"w-5 h-5 text-red-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Disabled Callbacks"}),(0,t.jsx)(l.Tooltip,{title:"Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Disabled Callbacks"}),(0,t.jsx)(s.Select,{mode:"multiple",placeholder:"Select callbacks to disable",value:f,onChange:e=>{let t=(0,p.mapDisplayToInternalNames)(e);b?.(t)},style:{width:"100%"},optionLabelProp:"label",children:j.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Select callbacks that should be disabled for this key. These callbacks will not receive any logging data."})]})]}),(0,t.jsx)(a.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(u.CogIcon,{className:"w-5 h-5 text-blue-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Logging Integrations"}),(0,t.jsx)(l.Tooltip,{title:"Configure callback logging integrations for this team.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsx)(i.Button,{variant:"secondary",onClick:()=>{v([...e,{callback_name:"",callback_type:"success",callback_vars:{}}])},icon:d.PlusIcon,size:"sm",className:"hover:border-blue-400 hover:text-blue-500",type:"button",children:"Add Integration"})]}),(0,t.jsx)("div",{className:"space-y-4",children:e.map((a,d)=>{let u=a.callback_name?Object.entries(p.callback_map).find(([e,t])=>t===a.callback_name)?.[0]:void 0,m=u?(0,g.resolveLogoSrc)(p.callbackInfo[u]?.logo):null;return(0,t.jsxs)(n.Card,{className:"border border-gray-200 shadow-xs hover:shadow-md transition-shadow duration-200",decoration:"top",decorationColor:"blue",children:[(0,t.jsxs)("div",{className:"flex justify-between items-start mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[m&&(0,t.jsx)("img",{src:m,alt:u,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("span",{className:"text-sm font-medium",children:[u||"New Integration"," Configuration"]})]}),(0,t.jsx)(i.Button,{variant:"light",onClick:()=>{v(e.filter((e,t)=>t!==d))},icon:c.TrashIcon,size:"xs",color:"red",className:"hover:bg-red-50",type:"button",children:"Remove"})]}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Integration Type"}),(0,t.jsx)(s.Select,{value:u,placeholder:"Select integration",onChange:e=>w(d,"callback_name",e),className:"w-full",optionLabelProp:"label",children:_.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Event Type"}),(0,t.jsxs)(s.Select,{value:a.callback_type,onChange:e=>w(d,"callback_type",e),className:"w-full",children:[(0,t.jsx)(x,{value:"success",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),(0,t.jsx)("span",{children:"Success Only"})]})}),(0,t.jsx)(x,{value:"failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-red-500 rounded-full"}),(0,t.jsx)("span",{children:"Failure Only"})]})}),(0,t.jsx)(x,{value:"success_and_failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),(0,t.jsx)("span",{children:"Success & Failure"})]})})]})]})]}),((e,s)=>{if(!e.callback_name)return null;let a=Object.entries(p.callback_map).find(([t,s])=>s===e.callback_name)?.[0];if(!a)return null;let i=p.callbackInfo[a]?.dynamic_params||{};return 0===Object.keys(i).length?null:(0,t.jsxs)("div",{className:"mt-6 pt-4 border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2 mb-4",children:[(0,t.jsx)("div",{className:"w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center",children:(0,t.jsx)("div",{className:"w-1.5 h-1.5 bg-blue-500 rounded-full"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Integration Parameters"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-4",children:Object.entries(i).map(([a,i])=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 capitalize flex items-center space-x-1",children:[(0,t.jsx)("span",{children:a.replace(/_/g," ")}),(0,t.jsx)(l.Tooltip,{title:`Environment variable reference recommended: os.environ/${a.toUpperCase()}`,children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),"password"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Sensitive"}),"number"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Number"})]}),"number"===i&&(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Value must be between 0 and 1"}),"number"===i?(0,t.jsx)(h.default,{step:.01,width:400,placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)}):(0,t.jsx)(o.TextInput,{type:"password"===i?"password":"text",placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)})]},a))})]})})(a,d)]})]},d)})}),0===e.length&&(0,t.jsxs)("div",{className:"text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50",children:[(0,t.jsx)(u.CogIcon,{className:"w-12 h-12 text-gray-300 mb-3 mx-auto"}),(0,t.jsx)("div",{className:"text-base font-medium mb-1",children:"No logging integrations configured"}),(0,t.jsx)("div",{className:"text-sm text-gray-400",children:'Click "Add Integration" to configure logging for this team'})]})]})}])},651904,e=>{"use strict";var t=e.i(843476),s=e.i(599724),l=e.i(266484);e.s(["default",0,function({value:e,onChange:a,premiumUser:r=!1,disabledCallbacks:i=[],onDisabledCallbacksChange:n}){return r?(0,t.jsx)(l.default,{value:e,onChange:a,disabledCallbacks:i,onDisabledCallbacksChange:n}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex flex-wrap gap-2 mb-3",children:[(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ langfuse-logging"}),(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ datadog-logging"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(s.Text,{className:"text-sm text-yellow-800",children:["Setting Key/Team logging settings is a LiteLLM Enterprise feature. Global Logging Settings are available for all free users. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})}])},939510,e=>{"use strict";var t=e.i(843476),s=e.i(808613),l=e.i(199133),a=e.i(592968),r=e.i(827252);let{Option:i}=l.Select;e.s(["default",0,({type:e,name:n,showDetailedDescriptions:o=!0,className:d="",initialValue:c=null,form:u,onChange:m})=>{let p=e.toUpperCase(),g=e.toLowerCase(),h=`Select 'guaranteed_throughput' to prevent overallocating ${p} limit when the key belongs to a Team with specific ${p} limits.`;return(0,t.jsx)(s.Form.Item,{label:(0,t.jsxs)("span",{children:[p," Rate Limit Type"," ",(0,t.jsx)(a.Tooltip,{title:h,children:(0,t.jsx)(r.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:n,initialValue:c,className:d,children:(0,t.jsx)(l.Select,{defaultValue:o?"default":void 0,placeholder:"Select rate limit type",style:{width:"100%"},optionLabelProp:o?"label":void 0,onChange:e=>{u&&u.setFieldValue(n,e),m&&m(e)},children:o?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",label:"Default",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Default"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Best effort throughput - no error if we're overallocating ",g," (Team/Key Limits checked at runtime)."]})]})}),(0,t.jsx)(i,{value:"guaranteed_throughput",label:"Guaranteed throughput",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Guaranteed throughput"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Guaranteed throughput - raise an error if we're overallocating ",g," (also checks model-specific limits)"]})]})}),(0,t.jsx)(i,{value:"dynamic",label:"Dynamic",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Dynamic"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["If the key has a set ",p," (e.g. 2 ",p,") and there are no 429 errors, it can dynamically exceed the limit when the model being called is not erroring."]})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",children:"Best effort throughput"}),(0,t.jsx)(i,{value:"guaranteed_throughput",children:"Guaranteed throughput"}),(0,t.jsx)(i,{value:"dynamic",children:"Dynamic"})]})})})}])},460285,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(404206),a=e.i(723731),r=e.i(653824),i=e.i(881073),n=e.i(197647),o=e.i(602869),d=e.i(158392),c=e.i(419470),u=e.i(695411);let m=(0,s.forwardRef)(({accessToken:e,value:m,onChange:p,modelData:g},h)=>{let[x,y]=(0,s.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[f,b]=(0,s.useState)([]),[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)([]),[N,k]=(0,s.useState)([]),[S,C]=(0,s.useState)({}),[T,I]=(0,s.useState)({}),A=(0,s.useRef)(!1),L=(0,s.useRef)(null);(0,s.useEffect)(()=>{let e=m?.router_settings?JSON.stringify({routing_strategy:m.router_settings.routing_strategy,fallbacks:m.router_settings.fallbacks,enable_tag_filtering:m.router_settings.enable_tag_filtering}):null;if(A.current&&e===L.current){A.current=!1;return}if(A.current&&e!==L.current&&(A.current=!1),e!==L.current)if(L.current=e,m?.router_settings){let e=m.router_settings,{fallbacks:t,...s}=e;y({routerSettings:s,selectedStrategy:e.routing_strategy||null,enableTagFiltering:e.enable_tag_filtering??!1});let l=e.fallbacks||[];b(l),j(l&&0!==l.length?l.map((e,t)=>{let[s,l]=Object.entries(e)[0];return{id:(t+1).toString(),primaryModel:s||null,fallbackModels:l||[]}}):[{id:"1",primaryModel:null,fallbackModels:[]}])}else y({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),b([]),j([{id:"1",primaryModel:null,fallbackModels:[]}])},[m]),(0,s.useEffect)(()=>{e&&(0,o.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),C(t);let s=e.fields.find(e=>"routing_strategy"===e.field_name);s?.options&&k(s.options),e.routing_strategy_descriptions&&I(e.routing_strategy_descriptions)}})},[e]),(0,s.useEffect)(()=>{e&&(async()=>{try{let t=await (0,u.fetchAvailableModels)(e);w(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}})()},[e]);let F=()=>{let e=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),t=new Set(["model_group_alias","retry_policy"]),s=Object.fromEntries(Object.entries({...x.routerSettings,enable_tag_filtering:x.enableTagFiltering,routing_strategy:x.selectedStrategy,fallbacks:f.length>0?f:null}).map(([s,l])=>{if("routing_strategy_args"!==s&&"routing_strategy"!==s&&"enable_tag_filtering"!==s&&"fallbacks"!==s){let a=document.querySelector(`input[name="${s}"]`);if(a){if(void 0!==a.value&&""!==a.value){let r=((s,l,a)=>{if(null==l)return a;let r=String(l).trim();if(""===r||"null"===r.toLowerCase())return null;if(e.has(s)){let e=Number(r);return Number.isNaN(e)?a:e}if(t.has(s)){if(""===r)return null;try{return JSON.parse(r)}catch{return a}}return"true"===r.toLowerCase()||"false"!==r.toLowerCase()&&r})(s,a.value,l);return[s,r]}return[s,null]}}else if("routing_strategy"===s)return[s,x.selectedStrategy];else if("enable_tag_filtering"===s)return[s,x.enableTagFiltering];else if("fallbacks"===s)return[s,f.length>0?f:null];else if("routing_strategy_args"===s&&"latency-based-routing"===x.selectedStrategy){let e=document.querySelector('input[name="lowest_latency_buffer"]'),t=document.querySelector('input[name="ttl"]'),s={};return e?.value&&(s.lowest_latency_buffer=Number(e.value)),t?.value&&(s.ttl=Number(t.value)),["routing_strategy_args",Object.keys(s).length>0?s:null]}return[s,l]}).filter(e=>null!=e)),l=(e,t=!1)=>null==e||"object"==typeof e&&!Array.isArray(e)&&0===Object.keys(e).length||t&&("number"!=typeof e||Number.isNaN(e))?null:e;return{routing_strategy:l(s.routing_strategy),allowed_fails:l(s.allowed_fails,!0),cooldown_time:l(s.cooldown_time,!0),num_retries:l(s.num_retries,!0),timeout:l(s.timeout,!0),retry_after:l(s.retry_after,!0),fallbacks:f.length>0?f:null,context_window_fallbacks:l(s.context_window_fallbacks),retry_policy:l(s.retry_policy),model_group_alias:l(s.model_group_alias),enable_tag_filtering:x.enableTagFiltering,routing_strategy_args:l(s.routing_strategy_args)}};(0,s.useEffect)(()=>{if(!p)return;let e=setTimeout(()=>{A.current=!0,p({router_settings:F()})},100);return()=>clearTimeout(e)},[x,f]);let M=Array.from(new Set(v.map(e=>e.model_group))).sort();return((0,s.useImperativeHandle)(h,()=>({getValue:()=>({router_settings:F()})})),e)?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(r.TabGroup,{className:"w-full",children:[(0,t.jsxs)(i.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(n.Tab,{value:"2",children:"Fallbacks"})]}),(0,t.jsxs)(a.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(d.default,{value:x,onChange:y,routerFieldsMetadata:S,availableRoutingStrategies:N,routingStrategyDescriptions:T})}),(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(c.FallbackSelectionForm,{groups:_,onGroupsChange:e=>{j(e),b(e.filter(e=>e.primaryModel&&e.fallbackModels.length>0).map(e=>({[e.primaryModel]:e.fallbackModels})))},availableModels:M,maxGroups:5})})]})]})}):null});m.displayName="RouterSettingsAccordion",e.s(["default",0,m])},363256,e=>{"use strict";var t=e.i(843476),s=e.i(199133);let{Text:l}=e.i(898586).Typography;e.s(["default",0,({organizations:e,value:a,onChange:r,disabled:i,loading:n,style:o})=>(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"All Organizations",value:a,onChange:r,disabled:i,loading:n,allowClear:!0,style:{minWidth:280,...o},filterOption:(t,s)=>{if(!s)return!1;let l=e?.find(e=>e.organization_id===s.key);if(!l)return!1;let a=t.toLowerCase().trim(),r=(l.organization_alias||"").toLowerCase(),i=(l.organization_id||"").toLowerCase();return r.includes(a)||i.includes(a)},children:e?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.organization_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.organization_alias})," ",(0,t.jsxs)(l,{type:"secondary",children:["(",e.organization_id,")"]})]},e.organization_id))})])},575260,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(482725),a=e.i(56456);e.s(["default",0,({projects:e,value:r,onChange:i,disabled:n,loading:o,teamId:d})=>{let c=d?e?.filter(e=>e.team_id===d):e;return(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"Search or select a project",value:r,onChange:i,disabled:n,loading:o,allowClear:!0,notFoundContent:o?(0,t.jsx)(l.Spin,{indicator:(0,t.jsx)(a.LoadingOutlined,{spin:!0}),size:"small"}):void 0,filterOption:(e,t)=>{if(!t)return!1;let s=c?.find(e=>e.project_id===t.key);if(!s)return!1;let l=e.toLowerCase().trim(),a=(s.project_alias||"").toLowerCase(),r=(s.project_id||"").toLowerCase();return a.includes(l)||r.includes(l)},optionFilterProp:"children",children:!o&&c?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.project_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.project_alias||e.project_id})," ",(0,t.jsxs)("span",{className:"text-gray-500",children:["(",e.project_id,")"]})]},e.project_id))})}])},128233,319312,e=>{"use strict";var t=e.i(843476),s=e.i(464571),l=e.i(199133),a=e.i(592968),r=e.i(425063),i=e.i(107233),n=e.i(37727),o=e.i(271645);e.s(["BudgetFallbacksEditor",0,function({value:e,onChange:d,availableModels:c}){let[u,m]=(0,o.useState)(()=>{let t;return 0===(t=Object.keys(e)).length?[]:t.map((t,s)=>({id:String(s+1),primaryModel:t,fallbackModels:e[t]}))}),p=e=>{m(e),d(Object.fromEntries(e.filter(e=>null!==e.primaryModel&&e.fallbackModels.length>0).map(e=>[e.primaryModel,e.fallbackModels])))},g=()=>{p([...u,{id:Date.now().toString(),primaryModel:null,fallbackModels:[]}])},h=(e,t)=>{p(u.map(s=>s.id===e?{...s,...t}:s))},x=new Set(u.map(e=>e.primaryModel).filter(Boolean));return 0===u.length?(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-2",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),u.map(e=>{let s=c.filter(t=>t===e.primaryModel||!x.has(t)),i=c.filter(t=>t!==e.primaryModel);return(0,t.jsxs)("div",{className:"relative rounded-lg border border-gray-200 bg-gray-50 p-4",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{var t;return t=e.id,void p(u.filter(e=>e.id!==t))},className:"absolute top-2 right-2 text-gray-400 hover:text-red-500 transition-colors p-1",children:(0,t.jsx)(n.X,{className:"w-4 h-4"})}),(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Primary Model"}),(0,t.jsx)(l.Select,{className:"w-full",placeholder:"Select model",value:e.primaryModel,onChange:t=>{let s=e.fallbackModels.filter(e=>e!==t);h(e.id,{primaryModel:t,fallbackModels:s})},showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:s.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-1 mb-2",children:(0,t.jsxs)("div",{className:"bg-amber-50 text-amber-600 px-3 py-0.5 rounded-full text-[10px] font-bold border border-amber-100 flex items-center gap-1",children:[(0,t.jsx)(r.ArrowDown,{className:"w-3 h-3"}),"IF BUDGET EXCEEDED, TRY"]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Fallback Models"}),(0,t.jsx)(l.Select,{mode:"multiple",className:"w-full",placeholder:e.primaryModel?"Select fallback models":"Select a primary model first",value:e.fallbackModels,onChange:t=>h(e.id,{fallbackModels:t}),disabled:!e.primaryModel,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:i.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(a.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})}),e.fallbackModels.length>1&&(0,t.jsx)("div",{className:"text-[10px] text-gray-400 mt-1 ml-1",children:"Tried in order; first model still within its own budget is used"})]})]},e.id)}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]})}],128233);var d=e.i(28651);let c=[{value:"1h",label:"Hourly",resetHint:"Resets every hour"},{value:"24h",label:"Daily",resetHint:"Resets daily at midnight UTC"},{value:"7d",label:"Weekly",resetHint:"Resets every Sunday at midnight UTC"},{value:"30d",label:"Monthly",resetHint:"Resets on the 1st of every month at midnight UTC"}];e.s(["BudgetWindowsEditor",0,function({value:e,onChange:a}){let r=(t,s,l)=>{a(e.map((e,a)=>a===t?{...e,[s]:l}:e))};return(0,t.jsxs)("div",{children:[e.map((i,n)=>{let o=c.find(e=>e.value===i.budget_duration)?.resetHint;return(0,t.jsxs)("div",{style:{marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsx)(l.Select,{value:i.budget_duration,onChange:e=>r(n,"budget_duration",e),style:{width:130},options:c.map(e=>({value:e.value,label:e.label}))}),(0,t.jsx)(d.InputNumber,{step:.01,min:0,precision:2,value:i.max_budget??void 0,onChange:e=>r(n,"max_budget",e??null),placeholder:"Max spend ($)",style:{width:160},prefix:"$"}),(0,t.jsx)(s.Button,{type:"text",danger:!0,size:"small",onClick:()=>{a(e.filter((e,t)=>t!==n))},style:{padding:"0 4px"},children:"✕"})]}),o&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#888",marginTop:3,marginLeft:2},children:["↻ ",o]})]},n)}),(0,t.jsx)(s.Button,{size:"small",onClick:t=>{t.preventDefault(),a([...e,{budget_duration:"24h",max_budget:null}])},children:"+ Add Budget Window"})]})}],319312)},390605,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(602869),a=e.i(599724),r=e.i(482725),i=e.i(91739),n=e.i(500727),o=e.i(531516),d=e.i(696609);e.s(["default",0,({accessToken:e,selectedServers:c,toolPermissions:u,onChange:m,disabled:p=!1})=>{let{data:g=[]}=(0,n.useMCPServers)(),[h,x]=(0,s.useState)({}),[y,f]=(0,s.useState)({}),[b,_]=(0,s.useState)({}),[j,v]=(0,s.useState)({}),w=(0,s.useRef)(u);(0,s.useEffect)(()=>{w.current=u},[u]);let N=(0,s.useMemo)(()=>0===c.length?[]:g.filter(e=>c.includes(e.server_id)),[g,c]),k=async(e,t)=>{f(t=>({...t,[e]:!0})),_(t=>({...t,[e]:""}));try{let s=await (0,l.listMCPTools)(t,e);if(s.error)_(t=>({...t,[e]:s.message||"Failed to fetch tools"})),x(t=>({...t,[e]:[]}));else{let t=s.tools||[];x(s=>({...s,[e]:t}));let l=w.current;if(!l[e]&&t.length>0){let s=t.filter(e=>"delete"!==(0,d.classifyToolOp)(e.name,e.description||"")).map(e=>e.name);m({...l,[e]:s})}}}catch(t){console.error(`Error fetching tools for server ${e}:`,t),_(t=>({...t,[e]:"Failed to fetch tools"})),x(t=>({...t,[e]:[]}))}finally{f(t=>({...t,[e]:!1}))}};(0,s.useEffect)(()=>{N.forEach(t=>{h[t.server_id]||y[t.server_id]||k(t.server_id,e)})},[N,e]);let S=(e,t)=>{m({...u,[e]:t})};return 0===c.length?null:(0,t.jsx)("div",{className:"space-y-4",children:N.map(e=>{let s=e.server_name||e.alias||e.server_id,l=h[e.server_id]||[],n=u[e.server_id]||[],d=y[e.server_id],c=b[e.server_id],g=j[e.server_id]??"crud";return(0,t.jsxs)("div",{className:"border rounded-lg bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-4 border-b bg-white rounded-t-lg",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-semibold text-gray-900",children:s}),e.description&&(0,t.jsx)(a.Text,{className:"text-sm text-gray-500",children:e.description})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[!p&&l.length>0&&(0,t.jsx)(i.Radio.Group,{value:g,onChange:t=>v(s=>({...s,[e.server_id]:t.target.value})),size:"small",optionType:"button",buttonStyle:"solid",options:[{label:"Risk Groups",value:"crud"},{label:"Flat List",value:"flat"}]}),!p&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;let s;return s=h[t=e.server_id]||[],void m({...u,[t]:s.map(e=>e.name)})},disabled:d,children:"Select All"}),(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;return t=e.server_id,void m({...u,[t]:[]})},disabled:d,children:"Deselect All"})]})]})]}),(0,t.jsxs)("div",{className:"p-4",children:[d&&(0,t.jsxs)("div",{className:"flex items-center justify-center py-8",children:[(0,t.jsx)(r.Spin,{size:"large"}),(0,t.jsx)(a.Text,{className:"ml-3 text-gray-500",children:"Loading tools..."})]}),c&&!d&&(0,t.jsxs)("div",{className:"p-4 bg-red-50 border border-red-200 rounded-lg text-center",children:[(0,t.jsx)(a.Text,{className:"text-red-600 font-medium",children:"Unable to load tools"}),(0,t.jsx)(a.Text,{className:"text-sm text-red-500 mt-1",children:c})]}),!d&&!c&&l.length>0&&"crud"===g&&(0,t.jsx)(o.default,{tools:l,value:u[e.server_id]?n:void 0,onChange:t=>S(e.server_id,t),readOnly:p}),!d&&!c&&l.length>0&&"flat"===g&&(0,t.jsx)("div",{className:"space-y-2",children:l.map(s=>{let l=n.includes(s.name);return(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("input",{type:"checkbox",checked:l,onChange:()=>{if(p)return;let t=l?n.filter(e=>e!==s.name):[...n,s.name];S(e.server_id,t)},disabled:p,className:"mt-0.5"}),(0,t.jsx)("div",{className:"flex-1 min-w-0",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900",children:s.name}),(0,t.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["- ",s.description||"No description"]})]})})]},s.name)})}),!d&&!c&&0===l.length&&(0,t.jsx)("div",{className:"text-center py-6",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"No tools available"})})]})]},e.server_id)})})}])},364769,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(237016),a=e.i(464571),r=e.i(888259);e.s(["default",0,({apiKey:e})=>{let[i,n]=(0,s.useState)(!1);return(0,t.jsxs)("div",{children:[(0,t.jsxs)("p",{className:"mb-2",children:["Please save this secret key somewhere safe and accessible. For security reasons,"," ",(0,t.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]}),(0,t.jsx)("p",{className:"text-sm text-gray-600 mt-3 mb-1",children:"Virtual Key:"}),(0,t.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,t.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal",margin:0},children:e})}),(0,t.jsx)(l.CopyToClipboard,{text:e,onCopy:()=>{n(!0),r.default.success("Key copied to clipboard"),setTimeout(()=>n(!1),2e3)},children:(0,t.jsx)(a.Button,{type:"primary",style:{marginTop:12},children:i?"Copied!":"Copy Virtual Key"})})]})}])},702597,e=>{"use strict";var t=e.i(843476),s=e.i(207082),l=e.i(109799),a=e.i(510674),r=e.i(109034),i=e.i(292639),n=e.i(135214),o=e.i(500330),d=e.i(827252),c=e.i(912598),u=e.i(677667),m=e.i(130643),p=e.i(898667),g=e.i(994388),h=e.i(309426),x=e.i(350967),y=e.i(599724),f=e.i(779241),b=e.i(629569),_=e.i(464571),j=e.i(808613),v=e.i(311451),w=e.i(212931),N=e.i(91739),k=e.i(199133),S=e.i(790848),C=e.i(262218),T=e.i(592968),I=e.i(898586),A=e.i(374009),L=e.i(271645),F=e.i(708347),M=e.i(552130),O=e.i(557662),E=e.i(9314),P=e.i(860585),B=e.i(82946),$=e.i(392110),R=e.i(533882),D=e.i(844565),V=e.i(651904),z=e.i(939510),U=e.i(460285),G=e.i(663435),K=e.i(363256),q=e.i(575260),W=e.i(371455),H=e.i(128233),Q=e.i(319312),J=e.i(355619),Y=e.i(75921),X=e.i(234713),Z=e.i(390605),ee=e.i(727749),et=e.i(602869),es=e.i(364769),el=e.i(435451),ea=e.i(916940);let{Option:er}=k.Select,ei=async(e,t,s,l)=>{try{if(null===e||null===t)return[];if(null!==s)return(await (0,et.modelAvailableCall)(s,e,t,!0,l,!0)).data.map(e=>e.id);return[]}catch(e){return console.error("Error fetching user models:",e),[]}},en=async(e,t,s,l)=>{try{if(null===e||null===t)return;if(null!==s){let a=(await (0,et.modelAvailableCall)(s,e,t)).data.map(e=>e.id);l(a)}}catch(e){console.error("Error fetching user models:",e)}};e.s(["default",0,({team:e,teams:eo,data:ed,addKey:ec,autoOpenCreate:eu,prefillData:em})=>{let{accessToken:ep,userId:eg,userRole:eh,premiumUser:ex}=(0,n.default)(),ey=ex||null!=eh&&F.rolesWithWriteAccess.includes(eh),{data:ef,isLoading:eb}=(0,l.useOrganizations)(),{data:e_,isLoading:ej}=(0,a.useProjects)(),{data:ev}=(0,i.useUISettings)(),{data:ew}=(0,r.useTags)(),eN=!!ev?.values?.enable_projects_ui,ek=!!ev?.values?.disable_custom_api_keys,eS=ew?Object.values(ew).map(e=>({value:e.name,label:e.name})):[],eC=(0,c.useQueryClient)(),[eT]=j.Form.useForm(),[eI,eA]=(0,L.useState)(!1),[eL,eF]=(0,L.useState)(null),[eM,eO]=(0,L.useState)(null),[eE,eP]=(0,L.useState)([]),[eB,e$]=(0,L.useState)([]),[eR,eD]=(0,L.useState)("you"),[eV,ez]=(0,L.useState)(!1),[eU,eG]=(0,L.useState)(null),[eK,eq]=(0,L.useState)([]),[eW,eH]=(0,L.useState)([]),[eQ,eJ]=(0,L.useState)([]),[eY,eX]=(0,L.useState)([]),[eZ,e0]=(0,L.useState)(e),[e1,e2]=(0,L.useState)(null),[e4,e3]=(0,L.useState)(null),[e5,e6]=(0,L.useState)(!1),[e7,e9]=(0,L.useState)(null),[e8,te]=(0,L.useState)({}),[tt,ts]=(0,L.useState)([]),[tl,ta]=(0,L.useState)(!1),[tr,ti]=(0,L.useState)([]),[tn,to]=(0,L.useState)([]),[td,tc]=(0,L.useState)("llm_api"),[tu,tm]=(0,L.useState)({}),[tp,tg]=(0,L.useState)(!1),[th,tx]=(0,L.useState)("30d"),[ty,tf]=(0,L.useState)(null),[tb,t_]=(0,L.useState)([]),[tj,tv]=(0,L.useState)({}),[tw,tN]=(0,L.useState)(0),[tk,tS]=(0,L.useState)(0),[tC,tT]=(0,L.useState)([]),[tI,tA]=(0,L.useState)(null),tL=()=>{eA(!1),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)},tF=()=>{eA(!1),eF(null),e0(null),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)};(0,L.useEffect)(()=>{eg&&eh&&ep&&en(eg,eh,ep,eP)},[ep,eg,eh]),(0,L.useEffect)(()=>{ep&&(0,et.getAgentsList)(ep).then(e=>tT(e?.agents||[])).catch(()=>tT([]))},[ep]),(0,L.useEffect)(()=>{let e=async()=>{try{let e=(await (0,et.getPoliciesList)(ep)).policies.map(e=>e.policy_name);eH(e)}catch(e){console.error("Failed to fetch policies:",e)}},t=async()=>{try{let e=await (0,et.getPromptsList)(ep);eJ(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}};(async()=>{try{let e=(await (0,et.getGuardrailsList)(ep)).guardrails.map(e=>e.guardrail_name);eq(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})(),e(),t()},[ep]),(0,L.useEffect)(()=>{(async()=>{try{if(ep){let e=sessionStorage.getItem("possibleUserRoles");if(e)te(JSON.parse(e));else{let e=await (0,et.getPossibleUserRoles)(ep);sessionStorage.setItem("possibleUserRoles",JSON.stringify(e)),te(e)}}}catch(e){console.error("Error fetching possible user roles:",e)}})()},[ep]),(0,L.useEffect)(()=>{if(eu&&!eV&&eo&&eh&&F.rolesWithWriteAccess.includes(eh)&&(eA(!0),ez(!0),em)){if(em.owned_by&&("another_user"===em.owned_by&&"Admin"!==eh?eD("you"):eD(em.owned_by)),em.team_id){let e=eo?.find(e=>e.team_id===em.team_id)||null;e&&(e0(e),eT.setFieldsValue({team_id:em.team_id}))}em.key_alias&&eT.setFieldsValue({key_alias:em.key_alias}),em.models&&em.models.length>0&&eG(em.models),em.key_type&&(tc(em.key_type),eT.setFieldsValue({key_type:em.key_type}))}},[eu,em,eo,eV,eT,eh]);let tM=eB.includes("no-default-models")&&!eZ,tO=async e=>{try{let t,l=e?.key_alias??"",a=e?.team_id??null;if((ed?.filter(e=>e.team_id===a).map(e=>e.key_alias)??[]).includes(l))throw Error(`Key alias ${l} already exists for team with ID ${a}, please provide another key alias`);if(ee.default.info("Making API Call"),eA(!0),"you"===eR)e.user_id=eg;else if("agent"===eR){if(!tI)return void ee.default.fromBackend("Please select an agent");e.agent_id=tI}let r={};try{r=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}if("service_account"===eR&&(r.service_account_id=e.key_alias),eY.length>0&&(r={...r,logging:eY.filter(e=>e.callback_name)}),tn.length>0){let e=(0,O.mapDisplayToInternalNames)(tn);r={...r,litellm_disabled_callbacks:e}}if(tp&&(e.auto_rotate=!0,e.rotation_interval=th),e.duration&&""!==e.duration.trim()||(e.duration=null),e.metadata=JSON.stringify(r),e.disable_global_guardrails||delete e.disable_global_guardrails,e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission={vector_stores:e.allowed_vector_store_ids},delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{servers:t,accessGroups:s}=e.allowed_mcp_servers_and_groups;t&&t.length>0&&(e.object_permission.mcp_servers=t),s&&s.length>0&&(e.object_permission.mcp_access_groups=s),delete e.allowed_mcp_servers_and_groups}let i=e.mcp_tool_permissions||{};if(Object.keys(i).length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_tool_permissions=i),delete e.mcp_tool_permissions,e.allowed_mcp_access_groups&&e.allowed_mcp_access_groups.length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_access_groups=e.allowed_mcp_access_groups,delete e.allowed_mcp_access_groups),e.allowed_agents_and_groups&&(e.allowed_agents_and_groups.agents?.length>0||e.allowed_agents_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{agents:t,accessGroups:s}=e.allowed_agents_and_groups;t&&t.length>0&&(e.object_permission.agents=t),s&&s.length>0&&(e.object_permission.agent_access_groups=s),delete e.allowed_agents_and_groups}Object.keys(tu).length>0&&(e.aliases=JSON.stringify(tu)),ty?.router_settings&&Object.values(ty.router_settings).some(e=>null!=e&&""!==e)&&(e.router_settings=ty.router_settings);let n=tb.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);n.length>0&&(e.budget_limits=n),Object.keys(tj).length>0&&(e.budget_fallbacks=tj),t="service_account"===eR?await (0,et.keyCreateServiceAccountCall)(ep,e):await (0,et.keyCreateCall)(ep,eg,e),ec(t),eC.invalidateQueries({queryKey:s.keyKeys.lists()}),eF(t.key),eO(t.soft_budget),ee.default.success("Virtual Key Created"),eT.resetFields(),t_([]),tv({}),tN(e=>e+1),localStorage.removeItem("userData"+eg)}catch(t){let e=(e=>{let t;if(!(t=!e||"object"!=typeof e||e instanceof Error?String(e):JSON.stringify(e)).includes("/key/generate")&&!t.includes("KeyManagementRoutes.KEY_GENERATE"))return`Error creating the key: ${e}`;let s=t;try{if(!e||"object"!=typeof e||e instanceof Error){let e=t.match(/\{[\s\S]*\}/);if(e){let t=JSON.parse(e[0]),l=t?.error||t;l?.message&&(s=l.message)}}else{let t=e?.error||e;t?.message&&(s=t.message)}}catch(e){}return t.includes("team_member_permission_error")||s.includes("Team member does not have permissions")?"Team member does not have permission to generate key for this team. Ask your proxy admin to configure the team member permission settings.":`Error creating the key: ${e}`})(t);ee.default.fromBackend(e)}};(0,L.useEffect)(()=>{if(e4){let e=e_?.find(e=>e.project_id===e4);e$(e?.models??[]),eT.setFieldValue("models",[]);return}eg&&eh&&ep&&ei(eg,eh,ep,eZ?.team_id??null).then(e=>{e$(Array.from(new Set([...eZ?.models??[],...e])))}),eU||eT.setFieldValue("models",[]),eT.setFieldValue("allowed_mcp_servers_and_groups",{servers:[],accessGroups:[]})},[eZ,e4,ep,eg,eh,eT]),(0,L.useEffect)(()=>{if(!eU||0===eU.length||!eB||0===eB.length)return;let e=eU.filter(e=>eB.includes(e));e.length>0&&eT.setFieldsValue({models:e}),eG(null)},[eU,eB,eT]),(0,L.useEffect)(()=>{if(!e4||!eo)return;let e=e_?.find(e=>e.project_id===e4);if(!e?.team_id||eZ?.team_id===e.team_id)return;let t=eo.find(t=>t.team_id===e.team_id)||null;t&&(e0(t),eT.setFieldValue("team_id",t.team_id))},[eo,e4,e_]);let tE=async e=>{if(!e)return void ts([]);ta(!0);try{let t=new URLSearchParams;if(t.append("user_email",e),null==ep)return;let s=(await (0,et.userFilterUICall)(ep,t)).map(e=>({label:`${e.user_email} (${e.user_id})`,value:e.user_id,user:e}));ts(s)}catch(e){console.error("Error fetching users:",e),ee.default.fromBackend("Failed to search for users")}finally{ta(!1)}},tP=(0,L.useCallback)((0,A.default)(e=>tE(e),300),[ep]);return(0,t.jsxs)("div",{children:[eh&&F.rolesWithWriteAccess.includes(eh)&&(0,t.jsx)(g.Button,{className:"mx-auto",onClick:()=>eA(!0),"data-testid":"create-key-button",children:"+ Create New Key"}),(0,t.jsx)(w.Modal,{open:eI,width:1e3,footer:null,onOk:tL,onCancel:tF,children:(0,t.jsxs)(j.Form,{form:eT,onFinish:tO,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Ownership"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Owned By"," ",(0,t.jsx)(T.Tooltip,{title:"Select who will own this Virtual Key",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),className:"mb-4",children:(0,t.jsxs)(N.Radio.Group,{onChange:e=>eD(e.target.value),value:eR,children:[(0,t.jsx)(N.Radio,{value:"you",children:"You"}),(0,t.jsx)(N.Radio,{value:"service_account",children:"Service Account"}),"Admin"===eh&&(0,t.jsx)(N.Radio,{value:"another_user",children:"Another User"}),(0,t.jsxs)(N.Radio,{value:"agent",children:["Agent ",(0,t.jsx)(C.Tag,{color:"purple",children:"New"})]})]})}),"another_user"===eR&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["User ID"," ",(0,t.jsx)(T.Tooltip,{title:"The user who will own this key and be responsible for its usage",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"user_id",className:"mt-4",rules:[{required:"another_user"===eR,message:"Please input the user ID of the user you are assigning the key to"}],children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",marginBottom:"8px"},children:[(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Type email to search for users",filterOption:!1,onSearch:e=>{tP(e)},onSelect:(e,t)=>{let s;return s=t.user,void eT.setFieldsValue({user_id:s.user_id})},options:tt,loading:tl,allowClear:!0,style:{width:"100%"},notFoundContent:tl?"Searching...":"No users found"}),(0,t.jsx)(_.Button,{onClick:()=>e6(!0),style:{marginLeft:"8px"},children:"Create User"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Search by email to find users"})]})}),"agent"===eR&&(0,t.jsxs)("div",{className:"mt-4 p-4 bg-purple-50 border border-purple-200 rounded-md",children:[(0,t.jsx)("div",{className:"mb-3",children:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:["Select Agent ",(0,t.jsx)("span",{className:"text-red-500",children:"*"})]})}),(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Select an agent",style:{width:"100%"},value:tI,onChange:e=>tA(e),filterOption:(e,t)=>t?.label?.toLowerCase().includes(e.toLowerCase()),options:tC.map(e=>({label:e.agent_name||e.agent_id,value:e.agent_id}))}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"This key will be used by the selected agent to make requests to LiteLLM"})]}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(T.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",className:"mt-4",children:(0,t.jsx)(K.default,{organizations:ef,loading:eb,disabled:"Admin"!==eh,onChange:e=>{e2(e||null),e0(null),e3(null),eT.setFieldValue("team_id",void 0),eT.setFieldValue("project_id",void 0)}})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Team"," ",(0,t.jsx)(T.Tooltip,{title:"The team this key belongs to, which determines available models and budget limits",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"team_id",initialValue:e?e.team_id:null,className:"mt-4",rules:[{required:"service_account"===eR,message:"Please select a team for the service account"}],help:"service_account"===eR?"required":"",children:(0,t.jsx)(G.default,{disabled:null!==e4,organizationId:e1,onTeamSelect:e=>{e0(e),e3(null),eT.setFieldValue("project_id",void 0),e?.organization_id?(e2(e.organization_id),eT.setFieldValue("organization_id",e.organization_id)):e||(e2(null),eT.setFieldValue("organization_id",void 0))}})}),eN&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Project"," ",(0,t.jsx)(T.Tooltip,{title:"Assign this key to a project. Selecting a project will lock the team to the project's team.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"project_id",className:"mt-4",children:(0,t.jsx)(q.default,{projects:e_,teamId:eZ?.team_id,loading:ej||!eo,onChange:e=>{if(!e){e3(null),e0(null),eT.setFieldValue("team_id",void 0);return}e3(e)}})})]}),tM&&(0,t.jsx)("div",{className:"mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md",children:(0,t.jsx)(y.Text,{className:"text-blue-800 text-sm",children:"Please select a team to continue configuring your Virtual Key. If you do not see any teams, please contact your Proxy Admin to either provide you with access to models or to add you to a team."})}),!tM&&(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Details"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["you"===eR||"another_user"===eR?"Key Name":"Service Account ID"," ",(0,t.jsx)(T.Tooltip,{title:"you"===eR||"another_user"===eR?"A descriptive name to identify this key":"Unique identifier for this service account",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_alias",rules:[{required:!0,message:`Please input a ${"you"===eR?"key name":"service account ID"}`}],help:"required",children:(0,t.jsx)(f.TextInput,{placeholder:""})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Models"," ",(0,t.jsx)(T.Tooltip,{title:"Select which models this key can access. Choose 'All Team Models' to grant access to all models available to the team. Leave empty to allow access to all models.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",rules:[],help:"management"===td||"read_only"===td?"Models field is disabled for this key type":"optional - leave empty to allow access to all models",className:"mt-4",children:(0,t.jsxs)(k.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:"management"===td||"read_only"===td,onChange:e=>{e.includes("all-team-models")&&eT.setFieldsValue({models:["all-team-models"]})},children:[!e4&&(0,t.jsx)(er,{value:"all-team-models",children:"All Team Models"},"all-team-models"),eB.map(e=>(0,t.jsx)(er,{value:e,children:(0,J.getModelDisplayName)(e)},e))]})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Key Type"," ",(0,t.jsx)(T.Tooltip,{title:"Select the type of key to determine what routes and operations this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_type",initialValue:"llm_api",className:"mt-4",children:(0,t.jsxs)(k.Select,{defaultValue:"llm_api",placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",onChange:e=>{tc(e),("management"===e||"read_only"===e)&&eT.setFieldsValue({models:[]})},children:[(0,t.jsx)(er,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"AI APIs"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(er,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Management"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only management routes (user/team/key management)"})]})}),(0,t.jsx)(er,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Full Access"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})})]})})]}),!tM&&(0,t.jsx)("div",{className:"mb-8",children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)(b.Title,{className:"m-0",children:"Optional Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum amount in USD this key can spend. When reached, the key will be blocked from making further requests",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",help:`Budget cannot exceed team max budget: $${e?.max_budget!==null&&e?.max_budget!==void 0?e?.max_budget:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.max_budget&&s>e.max_budget)throw Error(`Budget cannot exceed team max budget: $${(0,o.formatNumberWithCommas)(e.max_budget,4)}`)}}],children:(0,t.jsx)(el.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(T.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",help:`Team Reset Budget: ${e?.budget_duration!==null&&e?.budget_duration!==void 0?e?.budget_duration:"None"}`,children:(0,t.jsx)(P.default,{onChange:e=>eT.setFieldValue("budget_duration",e)})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(T.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(Q.BudgetWindowsEditor,{value:tb,onChange:t_})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(T.Tooltip,{title:"When a model exceeds its per-model budget (model_max_budget), requests automatically reroute to fallback models instead of failing. Configure per-model budgets in Advanced Settings.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(H.BudgetFallbacksEditor,{value:tj,onChange:tv,availableModels:eB},tw)}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Tokens per minute Limit (TPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of tokens this key can process per minute. Helps control usage and costs",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tpm_limit",help:`TPM cannot exceed team TPM limit: ${e?.tpm_limit!==null&&e?.tpm_limit!==void 0?e?.tpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.tpm_limit&&s>e.tpm_limit)throw Error(`TPM limit cannot exceed team TPM limit: ${e.tpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"tpm",name:"tpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Requests per minute Limit (RPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of API requests this key can make per minute. Helps prevent abuse and manage load",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"rpm_limit",help:`RPM cannot exceed team RPM limit: ${e?.rpm_limit!==null&&e?.rpm_limit!==void 0?e?.rpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.rpm_limit&&s>e.rpm_limit)throw Error(`RPM limit cannot exceed team RPM limit: ${e.rpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"rpm",name:"rpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:ey?"Select existing guardrails or enter new ones":"Premium feature - Upgrade to set guardrails by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter guardrails":"Premium feature - Upgrade to set guardrails by key",options:eK.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"disable_global_guardrails",className:"mt-4",valuePropName:"checked",help:ey?"Bypass global guardrails for this key":"Premium feature - Upgrade to disable global guardrails by key",children:(0,t.jsx)(S.Switch,{disabled:!ey,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(T.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",className:"mt-4",help:ex?"Select existing policies or enter new ones":"Premium feature - Upgrade to set policies by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter policies":"Premium feature - Upgrade to set policies by key",options:eW.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Prompts"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific prompt templates",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/prompt_management",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"prompts",className:"mt-4",help:ex?"Select existing prompts or enter new ones":"Premium feature - Upgrade to set prompts by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:eQ.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(T.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",className:"mt-4",help:"Select access groups to assign to this key",children:(0,t.jsx)(E.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Pass Through Routes"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific pass through routes",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"allowed_passthrough_routes",className:"mt-4",help:ex?"Select existing pass through routes or enter new ones":"Premium feature - Upgrade to set pass through routes by key",children:(0,t.jsx)(D.default,{onChange:e=>eT.setFieldValue("allowed_passthrough_routes",e),value:eT.getFieldValue("allowed_passthrough_routes"),accessToken:ep,placeholder:ex?"Select or enter pass through routes":"Premium feature - Upgrade to set pass through routes by key",disabled:!ex,teamId:eZ?eZ.team_id:null})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,t.jsx)(T.Tooltip,{title:"Select which vector stores this key can access. If none selected, the key will have access to all available vector stores",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this key can access. Leave empty for access to all vector stores",children:(0,t.jsx)(ea.default,{onChange:e=>eT.setFieldValue("allowed_vector_store_ids",e),value:eT.getFieldValue("allowed_vector_store_ids"),accessToken:ep,placeholder:"Select vector stores (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Metadata"," ",(0,t.jsx)(T.Tooltip,{title:"JSON object with additional information about this key. Used for tracking or custom logic",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"metadata",className:"mt-4",children:(0,t.jsx)(v.Input.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Tags"," ",(0,t.jsx)(T.Tooltip,{title:"Tags for tracking spend and/or doing tag-based routing. Used for analytics and filtering",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tags",className:"mt-4",help:"Tags for tracking spend and/or doing tag-based routing.",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",tokenSeparators:[","],options:eS})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"MCP Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(T.Tooltip,{title:"Select which MCP servers or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",help:"Select MCP servers or access groups this key can access",children:(0,t.jsx)(Y.default,{onChange:e=>eT.setFieldValue("allowed_mcp_servers_and_groups",e),value:eT.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:ep,teamId:eZ?.team_id??null,placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(j.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(j.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(Z.default,{accessToken:ep,selectedServers:(eT.getFieldValue("allowed_mcp_servers_and_groups")?.servers||[]).filter(e=>e!==X.NO_MCP_SERVERS_SENTINEL),toolPermissions:eT.getFieldValue("mcp_tool_permissions")||{},onChange:e=>eT.setFieldsValue({mcp_tool_permissions:e})})})})]})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Agent Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Agents"," ",(0,t.jsx)(T.Tooltip,{title:"Select which agents or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_agents_and_groups",help:"Select agents or access groups this key can access",children:(0,t.jsx)(M.default,{onChange:e=>eT.setFieldValue("allowed_agents_and_groups",e),value:eT.getFieldValue("allowed_agents_and_groups"),accessToken:ep,placeholder:"Select agents or access groups (optional)"})})})]}),ex?(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!0,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]}):(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Key-level logging settings is an enterprise feature, get in touch -",(0,t.jsx)("a",{href:"https://www.litellm.ai/enterprise",target:"_blank",children:"https://www.litellm.ai/enterprise"})]}),placement:"top",children:(0,t.jsxs)("div",{style:{position:"relative"},children:[(0,t.jsx)("div",{style:{opacity:.5},children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!1,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]})}),(0,t.jsx)("div",{style:{position:"absolute",inset:0,cursor:"not-allowed"}})]})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Router Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4 w-full",children:(0,t.jsx)(U.default,{accessToken:ep||"",value:ty||void 0,onChange:tf,modelData:eE.length>0?{data:eE.map(e=>({model_name:e}))}:void 0},tk)})})]},`router-settings-accordion-${tk}`),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Model Aliases"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)(y.Text,{className:"text-sm text-gray-600 mb-4",children:"Create custom aliases for models that can be used in API calls. This allows you to create shortcuts for specific models."}),(0,t.jsx)(R.default,{accessToken:ep,initialModelAliases:tu,onAliasUpdate:tm,showExampleConfig:!1})]})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Key Lifecycle"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)($.default,{form:eT,autoRotationEnabled:tp,onAutoRotationChange:tg,rotationInterval:th,onRotationIntervalChange:tx,isCreateMode:!0})})}),(0,t.jsx)(j.Form.Item,{name:"duration",hidden:!0,initialValue:null,children:(0,t.jsx)(v.Input,{})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("b",{children:"Advanced Settings"}),(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Learn more about advanced settings in our"," ",(0,t.jsx)("a",{href:et.proxyBaseUrl?`${et.proxyBaseUrl}/#/key%20management/generate_key_fn_key_generate_post`:"/#/key%20management/generate_key_fn_key_generate_post",target:"_blank",rel:"noopener noreferrer",className:"text-blue-400 hover:text-blue-300",children:"documentation"})]}),children:(0,t.jsx)(d.InfoCircleOutlined,{className:"text-gray-400 hover:text-gray-300 cursor-help"})})]})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(B.default,{schemaComponent:"GenerateKeyRequest",form:eT,excludedFields:["key_alias","team_id","organization_id","models","duration","metadata","tags","guardrails","max_budget","budget_duration","tpm_limit","rpm_limit",...ek?["key"]:[]]})})]})]})]})}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(_.Button,{htmlType:"submit",disabled:tM,style:{opacity:tM?.5:1},children:"Create Key"})})]})}),e5&&(0,t.jsx)(w.Modal,{title:"Create New User",open:e5,onCancel:()=>e6(!1),footer:null,width:800,children:(0,t.jsx)(W.CreateUserButton,{userID:eg,accessToken:ep,teams:eo,possibleUIRoles:e8,onUserCreated:e=>{e9(e),eT.setFieldsValue({user_id:e}),e6(!1)},isEmbedded:!0})}),eL&&(0,t.jsx)(w.Modal,{open:eI,onOk:tL,onCancel:tF,footer:null,children:(0,t.jsxs)(x.Grid,{numItems:1,className:"gap-2 w-full",children:[(0,t.jsx)(b.Title,{children:"Save your Key"}),(0,t.jsx)(h.Col,{numColSpan:1,children:null!=eL?(0,t.jsx)(es.default,{apiKey:eL}):(0,t.jsx)(y.Text,{children:"Key being created, this might take 30s"})})]})})]})},"fetchTeamModels",0,ei,"fetchUserModels",0,en],702597)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/025gva_b59p-5.js b/litellm/proxy/_experimental/out/_next/static/chunks/025gva_b59p-5.js deleted file mode 100644 index c0fe3dcc751..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/025gva_b59p-5.js +++ /dev/null @@ -1,68 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,599724,936325,e=>{"use strict";var o=e.i(95779),r=e.i(444755),l=e.i(673706),n=e.i(271645);let t=n.default.forwardRef((e,t)=>{let{color:a,className:s,children:i}=e;return n.default.createElement("p",{ref:t,className:(0,r.tremorTwMerge)("text-tremor-default",a?(0,l.getColorClassNames)(a,o.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),s)},i)});t.displayName="Text",e.s(["default",0,t],936325),e.s(["Text",0,t],599724)},350967,46757,e=>{"use strict";var o=e.i(290571),r=e.i(444755),l=e.i(673706),n=e.i(271645);let t={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},a={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},s={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,t,"gridColsLg",0,i,"gridColsMd",0,s,"gridColsSm",0,a],46757);let c=(0,l.makeClassName)("Grid"),d=(e,o)=>e&&Object.keys(o).includes(String(e))?o[e]:"",g=n.default.forwardRef((e,l)=>{let{numItems:g=1,numItemsSm:p,numItemsMd:m,numItemsLg:h,children:u,className:b}=e,k=(0,o.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),f=d(g,t),x=d(p,a),v=d(m,s),w=d(h,i),y=(0,r.tremorTwMerge)(f,x,v,w);return n.default.createElement("div",Object.assign({ref:l,className:(0,r.tremorTwMerge)(c("root"),"grid",y,b)},k),u)});g.displayName="Grid",e.s(["Grid",0,g],350967)},678745,e=>{"use strict";let o=(0,e.i(475254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,o])},678784,e=>{"use strict";var o=e.i(678745);e.s(["CheckIcon",()=>o.default])},546467,e=>{"use strict";let o=(0,e.i(475254).default)("external-link",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);e.s(["default",0,o])},673709,e=>{"use strict";var o=e.i(843476),r=e.i(271645),l=e.i(678784);let n=(0,e.i(475254).default)("clipboard",[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1",key:"tgr4d6"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2",key:"116196"}]]);var t=e.i(650056);let a={'code[class*="language-"]':{background:"hsl(230, 1%, 98%)",color:"hsl(230, 8%, 24%)",fontFamily:'"Fira Code", "Fira Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace',direction:"ltr",textAlign:"left",whiteSpace:"pre",wordSpacing:"normal",wordBreak:"normal",lineHeight:"1.5",MozTabSize:"2",OTabSize:"2",tabSize:"2",WebkitHyphens:"none",MozHyphens:"none",msHyphens:"none",hyphens:"none"},'pre[class*="language-"]':{background:"hsl(230, 1%, 98%)",color:"hsl(230, 8%, 24%)",fontFamily:'"Fira Code", "Fira Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace',direction:"ltr",textAlign:"left",whiteSpace:"pre",wordSpacing:"normal",wordBreak:"normal",lineHeight:"1.5",MozTabSize:"2",OTabSize:"2",tabSize:"2",WebkitHyphens:"none",MozHyphens:"none",msHyphens:"none",hyphens:"none",padding:"1em",margin:"0.5em 0",overflow:"auto",borderRadius:"0.3em"},'code[class*="language-"]::-moz-selection':{background:"hsl(230, 1%, 90%)",color:"inherit"},'code[class*="language-"] *::-moz-selection':{background:"hsl(230, 1%, 90%)",color:"inherit"},'pre[class*="language-"] *::-moz-selection':{background:"hsl(230, 1%, 90%)",color:"inherit"},'code[class*="language-"]::selection':{background:"hsl(230, 1%, 90%)",color:"inherit"},'code[class*="language-"] *::selection':{background:"hsl(230, 1%, 90%)",color:"inherit"},'pre[class*="language-"] *::selection':{background:"hsl(230, 1%, 90%)",color:"inherit"},':not(pre) > code[class*="language-"]':{padding:"0.2em 0.3em",borderRadius:"0.3em",whiteSpace:"normal"},comment:{color:"hsl(230, 4%, 64%)",fontStyle:"italic"},prolog:{color:"hsl(230, 4%, 64%)"},cdata:{color:"hsl(230, 4%, 64%)"},doctype:{color:"hsl(230, 8%, 24%)"},punctuation:{color:"hsl(230, 8%, 24%)"},entity:{color:"hsl(230, 8%, 24%)",cursor:"help"},"attr-name":{color:"hsl(35, 99%, 36%)"},"class-name":{color:"hsl(35, 99%, 36%)"},boolean:{color:"hsl(35, 99%, 36%)"},constant:{color:"hsl(35, 99%, 36%)"},number:{color:"hsl(35, 99%, 36%)"},atrule:{color:"hsl(35, 99%, 36%)"},keyword:{color:"hsl(301, 63%, 40%)"},property:{color:"hsl(5, 74%, 59%)"},tag:{color:"hsl(5, 74%, 59%)"},symbol:{color:"hsl(5, 74%, 59%)"},deleted:{color:"hsl(5, 74%, 59%)"},important:{color:"hsl(5, 74%, 59%)"},selector:{color:"hsl(119, 34%, 47%)"},string:{color:"hsl(119, 34%, 47%)"},char:{color:"hsl(119, 34%, 47%)"},builtin:{color:"hsl(119, 34%, 47%)"},inserted:{color:"hsl(119, 34%, 47%)"},regex:{color:"hsl(119, 34%, 47%)"},"attr-value":{color:"hsl(119, 34%, 47%)"},"attr-value > .token.punctuation":{color:"hsl(119, 34%, 47%)"},variable:{color:"hsl(221, 87%, 60%)"},operator:{color:"hsl(221, 87%, 60%)"},function:{color:"hsl(221, 87%, 60%)"},url:{color:"hsl(198, 99%, 37%)"},"attr-value > .token.punctuation.attr-equals":{color:"hsl(230, 8%, 24%)"},"special-attr > .token.attr-value > .token.value.css":{color:"hsl(230, 8%, 24%)"},".language-css .token.selector":{color:"hsl(5, 74%, 59%)"},".language-css .token.property":{color:"hsl(230, 8%, 24%)"},".language-css .token.function":{color:"hsl(198, 99%, 37%)"},".language-css .token.url > .token.function":{color:"hsl(198, 99%, 37%)"},".language-css .token.url > .token.string.url":{color:"hsl(119, 34%, 47%)"},".language-css .token.important":{color:"hsl(301, 63%, 40%)"},".language-css .token.atrule .token.rule":{color:"hsl(301, 63%, 40%)"},".language-javascript .token.operator":{color:"hsl(301, 63%, 40%)"},".language-javascript .token.template-string > .token.interpolation > .token.interpolation-punctuation.punctuation":{color:"hsl(344, 84%, 43%)"},".language-json .token.operator":{color:"hsl(230, 8%, 24%)"},".language-json .token.null.keyword":{color:"hsl(35, 99%, 36%)"},".language-markdown .token.url":{color:"hsl(230, 8%, 24%)"},".language-markdown .token.url > .token.operator":{color:"hsl(230, 8%, 24%)"},".language-markdown .token.url-reference.url > .token.string":{color:"hsl(230, 8%, 24%)"},".language-markdown .token.url > .token.content":{color:"hsl(221, 87%, 60%)"},".language-markdown .token.url > .token.url":{color:"hsl(198, 99%, 37%)"},".language-markdown .token.url-reference.url":{color:"hsl(198, 99%, 37%)"},".language-markdown .token.blockquote.punctuation":{color:"hsl(230, 4%, 64%)",fontStyle:"italic"},".language-markdown .token.hr.punctuation":{color:"hsl(230, 4%, 64%)",fontStyle:"italic"},".language-markdown .token.code-snippet":{color:"hsl(119, 34%, 47%)"},".language-markdown .token.bold .token.content":{color:"hsl(35, 99%, 36%)"},".language-markdown .token.italic .token.content":{color:"hsl(301, 63%, 40%)"},".language-markdown .token.strike .token.content":{color:"hsl(5, 74%, 59%)"},".language-markdown .token.strike .token.punctuation":{color:"hsl(5, 74%, 59%)"},".language-markdown .token.list.punctuation":{color:"hsl(5, 74%, 59%)"},".language-markdown .token.title.important > .token.punctuation":{color:"hsl(5, 74%, 59%)"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"},namespace:{Opacity:"0.8"},"token.tab:not(:empty):before":{color:"hsla(230, 8%, 24%, 0.2)"},"token.cr:before":{color:"hsla(230, 8%, 24%, 0.2)"},"token.lf:before":{color:"hsla(230, 8%, 24%, 0.2)"},"token.space:before":{color:"hsla(230, 8%, 24%, 0.2)"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item":{marginRight:"0.4em"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > button":{background:"hsl(230, 1%, 90%)",color:"hsl(230, 6%, 44%)",padding:"0.1em 0.4em",borderRadius:"0.3em"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > a":{background:"hsl(230, 1%, 90%)",color:"hsl(230, 6%, 44%)",padding:"0.1em 0.4em",borderRadius:"0.3em"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > span":{background:"hsl(230, 1%, 90%)",color:"hsl(230, 6%, 44%)",padding:"0.1em 0.4em",borderRadius:"0.3em"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover":{background:"hsl(230, 1%, 78%)",color:"hsl(230, 8%, 24%)"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus":{background:"hsl(230, 1%, 78%)",color:"hsl(230, 8%, 24%)"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover":{background:"hsl(230, 1%, 78%)",color:"hsl(230, 8%, 24%)"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus":{background:"hsl(230, 1%, 78%)",color:"hsl(230, 8%, 24%)"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover":{background:"hsl(230, 1%, 78%)",color:"hsl(230, 8%, 24%)"},"div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus":{background:"hsl(230, 1%, 78%)",color:"hsl(230, 8%, 24%)"},".line-highlight.line-highlight":{background:"hsla(230, 8%, 24%, 0.05)"},".line-highlight.line-highlight:before":{background:"hsl(230, 1%, 90%)",color:"hsl(230, 8%, 24%)",padding:"0.1em 0.6em",borderRadius:"0.3em",boxShadow:"0 2px 0 0 rgba(0, 0, 0, 0.2)"},".line-highlight.line-highlight[data-end]:after":{background:"hsl(230, 1%, 90%)",color:"hsl(230, 8%, 24%)",padding:"0.1em 0.6em",borderRadius:"0.3em",boxShadow:"0 2px 0 0 rgba(0, 0, 0, 0.2)"},"pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows > span:hover:before":{backgroundColor:"hsla(230, 8%, 24%, 0.05)"},".line-numbers.line-numbers .line-numbers-rows":{borderRightColor:"hsla(230, 8%, 24%, 0.2)"},".command-line .command-line-prompt":{borderRightColor:"hsla(230, 8%, 24%, 0.2)"},".line-numbers .line-numbers-rows > span:before":{color:"hsl(230, 1%, 62%)"},".command-line .command-line-prompt > span:before":{color:"hsl(230, 1%, 62%)"},".rainbow-braces .token.token.punctuation.brace-level-1":{color:"hsl(5, 74%, 59%)"},".rainbow-braces .token.token.punctuation.brace-level-5":{color:"hsl(5, 74%, 59%)"},".rainbow-braces .token.token.punctuation.brace-level-9":{color:"hsl(5, 74%, 59%)"},".rainbow-braces .token.token.punctuation.brace-level-2":{color:"hsl(119, 34%, 47%)"},".rainbow-braces .token.token.punctuation.brace-level-6":{color:"hsl(119, 34%, 47%)"},".rainbow-braces .token.token.punctuation.brace-level-10":{color:"hsl(119, 34%, 47%)"},".rainbow-braces .token.token.punctuation.brace-level-3":{color:"hsl(221, 87%, 60%)"},".rainbow-braces .token.token.punctuation.brace-level-7":{color:"hsl(221, 87%, 60%)"},".rainbow-braces .token.token.punctuation.brace-level-11":{color:"hsl(221, 87%, 60%)"},".rainbow-braces .token.token.punctuation.brace-level-4":{color:"hsl(301, 63%, 40%)"},".rainbow-braces .token.token.punctuation.brace-level-8":{color:"hsl(301, 63%, 40%)"},".rainbow-braces .token.token.punctuation.brace-level-12":{color:"hsl(301, 63%, 40%)"},"pre.diff-highlight > code .token.token.deleted:not(.prefix)":{backgroundColor:"hsla(353, 100%, 66%, 0.15)"},"pre > code.diff-highlight .token.token.deleted:not(.prefix)":{backgroundColor:"hsla(353, 100%, 66%, 0.15)"},"pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre.diff-highlight > code .token.token.deleted:not(.prefix) *::-moz-selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre > code.diff-highlight .token.token.deleted:not(.prefix) *::-moz-selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection":{backgroundColor:"hsla(353, 95%, 66%, 0.25)"},"pre.diff-highlight > code .token.token.inserted:not(.prefix)":{backgroundColor:"hsla(137, 100%, 55%, 0.15)"},"pre > code.diff-highlight .token.token.inserted:not(.prefix)":{backgroundColor:"hsla(137, 100%, 55%, 0.15)"},"pre.diff-highlight > code .token.token.inserted:not(.prefix)::-moz-selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre.diff-highlight > code .token.token.inserted:not(.prefix) *::-moz-selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre > code.diff-highlight .token.token.inserted:not(.prefix)::-moz-selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre > code.diff-highlight .token.token.inserted:not(.prefix) *::-moz-selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},"pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection":{backgroundColor:"hsla(135, 73%, 55%, 0.25)"},".prism-previewer.prism-previewer:before":{borderColor:"hsl(0, 0, 95%)"},".prism-previewer-gradient.prism-previewer-gradient div":{borderColor:"hsl(0, 0, 95%)",borderRadius:"0.3em"},".prism-previewer-color.prism-previewer-color:before":{borderRadius:"0.3em"},".prism-previewer-easing.prism-previewer-easing:before":{borderRadius:"0.3em"},".prism-previewer.prism-previewer:after":{borderTopColor:"hsl(0, 0, 95%)"},".prism-previewer-flipped.prism-previewer-flipped.after":{borderBottomColor:"hsl(0, 0, 95%)"},".prism-previewer-angle.prism-previewer-angle:before":{background:"hsl(0, 0%, 100%)"},".prism-previewer-time.prism-previewer-time:before":{background:"hsl(0, 0%, 100%)"},".prism-previewer-easing.prism-previewer-easing":{background:"hsl(0, 0%, 100%)"},".prism-previewer-angle.prism-previewer-angle circle":{stroke:"hsl(230, 8%, 24%)",strokeOpacity:"1"},".prism-previewer-time.prism-previewer-time circle":{stroke:"hsl(230, 8%, 24%)",strokeOpacity:"1"},".prism-previewer-easing.prism-previewer-easing circle":{stroke:"hsl(230, 8%, 24%)",fill:"transparent"},".prism-previewer-easing.prism-previewer-easing path":{stroke:"hsl(230, 8%, 24%)"},".prism-previewer-easing.prism-previewer-easing line":{stroke:"hsl(230, 8%, 24%)"}};e.s(["default",0,({code:e,language:s})=>{let[i,c]=(0,r.useState)(!1);return(0,o.jsxs)("div",{className:"relative rounded-lg border border-gray-200 overflow-hidden",children:[(0,o.jsx)("button",{onClick:()=>{navigator.clipboard.writeText(e),c(!0),setTimeout(()=>c(!1),2e3)},className:"absolute top-3 right-3 p-2 rounded-md bg-gray-100 hover:bg-gray-200 text-gray-600 z-10","aria-label":"Copy code",children:i?(0,o.jsx)(l.CheckIcon,{size:16}):(0,o.jsx)(n,{size:16})}),(0,o.jsx)(t.Prism,{language:s,style:a,customStyle:{margin:0,padding:"1.5rem",borderRadius:"0.5rem",fontSize:"0.9rem",backgroundColor:"#fafafa"},showLineNumbers:!0,children:e})]})}],673709)},778917,e=>{"use strict";var o=e.i(546467);e.s(["ExternalLink",()=>o.default])},191905,e=>{"use strict";var o=e.i(843476),r=e.i(599724),l=e.i(197647),n=e.i(653824),t=e.i(881073),a=e.i(404206),s=e.i(723731),i=e.i(350967),c=e.i(673709),d=e.i(778917),g=e.i(115504);let p=({href:e,className:r})=>(0,o.jsxs)("a",{href:e,target:"_blank",rel:"noopener noreferrer",title:"Open documentation in a new tab",className:(0,g.cn)("inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-white/80 px-3.5 py-2 text-sm font-medium text-zinc-700 shadow-xs","hover:bg-white focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-500 active:translate-y-[0.5px]",r),children:[(0,o.jsx)("span",{children:"API Reference Docs"}),(0,o.jsx)(d.ExternalLink,{"aria-hidden":!0,className:"h-4 w-4 opacity-80"}),(0,o.jsx)("span",{className:"sr-only",children:"(opens in a new tab)"})]}),m=({proxySettings:e})=>{let d="",g=e?.LITELLM_UI_API_DOC_BASE_URL;return g&&g.trim()?d=g:e?.PROXY_BASE_URL&&(d=e.PROXY_BASE_URL),(0,o.jsx)(o.Fragment,{children:(0,o.jsx)(i.Grid,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,o.jsxs)("div",{className:"mb-5",children:[(0,o.jsxs)("div",{className:"flex items-center justify-between",children:[(0,o.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,o.jsx)(p,{className:"ml-3 shrink-0",href:"https://docs.litellm.ai/docs/proxy/user_keys"})]}),(0,o.jsxs)(r.Text,{className:"mt-2 mb-2",children:["LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url to point to your litellm proxy. Example Below"," "]}),(0,o.jsxs)(n.TabGroup,{children:[(0,o.jsxs)(t.TabList,{children:[(0,o.jsx)(l.Tab,{children:"OpenAI Python SDK"}),(0,o.jsx)(l.Tab,{children:"LlamaIndex"}),(0,o.jsx)(l.Tab,{children:"Langchain Py"})]}),(0,o.jsxs)(s.TabPanels,{children:[(0,o.jsx)(a.TabPanel,{children:(0,o.jsx)(c.default,{language:"python",code:`import openai -client = openai.OpenAI( - api_key="your_api_key", - base_url="${d}" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys -) - -response = client.chat.completions.create( - model="gpt-3.5-turbo", # model to send to the proxy - messages = [ - { - "role": "user", - "content": "this is a test request, write a short poem" - } - ] -) - -print(response)`})}),(0,o.jsx)(a.TabPanel,{children:(0,o.jsx)(c.default,{language:"python",code:`import os, dotenv - -from llama_index.llms import AzureOpenAI -from llama_index.embeddings import AzureOpenAIEmbedding -from llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext - -llm = AzureOpenAI( - engine="azure-gpt-3.5", # model_name on litellm proxy - temperature=0.0, - azure_endpoint="${d}", # litellm proxy endpoint - api_key="sk-1234", # litellm proxy API Key - api_version="2023-07-01-preview", -) - -embed_model = AzureOpenAIEmbedding( - deployment_name="azure-embedding-model", - azure_endpoint="${d}", - api_key="sk-1234", - api_version="2023-07-01-preview", -) - -documents = SimpleDirectoryReader("llama_index_data").load_data() -service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model) -index = VectorStoreIndex.from_documents(documents, service_context=service_context) - -query_engine = index.as_query_engine() -response = query_engine.query("What did the author do growing up?") -print(response)`})}),(0,o.jsx)(a.TabPanel,{children:(0,o.jsx)(c.default,{language:"python",code:`from langchain.chat_models import ChatOpenAI -from langchain.prompts.chat import ( - ChatPromptTemplate, - HumanMessagePromptTemplate, - SystemMessagePromptTemplate, -) -from langchain.schema import HumanMessage, SystemMessage - -chat = ChatOpenAI( - openai_api_base="${d}", - model = "gpt-3.5-turbo", - temperature=0.1 -) - -messages = [ - SystemMessage( - content="You are a helpful assistant that im using to make a test request to." - ), - HumanMessage( - content="test from litellm. tell me why it's amazing in 1 sentence" - ), -] -response = chat(messages) - -print(response)`})})]})]})]})})})};var h=e.i(135214),u=e.i(592392);e.s(["default",0,()=>{let{accessToken:e}=(0,h.default)(),r=(0,u.default)(e);return(0,o.jsx)(m,{proxySettings:r})}],191905)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/027d2u2cl335o.js b/litellm/proxy/_experimental/out/_next/static/chunks/027d2u2cl335o.js new file mode 100644 index 00000000000..b410c1ed1a1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/027d2u2cl335o.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,772345,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let s={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"}}]},name:"sync",theme:"outlined"};var l=e.i(9583),i=a.forwardRef(function(e,i){return a.createElement(l.default,(0,t.default)({},e,{ref:i,icon:s}))});e.s(["SyncOutlined",0,i],772345)},11751,e=>{"use strict";e.s(["mapEmptyStringToNull",0,function(e){return""===e?null:e}])},643449,e=>{"use strict";var t=e.i(843476),a=e.i(262218),s=e.i(810757),l=e.i(477386),i=e.i(557662),r=e.i(555987);e.s(["default",0,function({loggingConfigs:e=[],disabledCallbacks:n=[],variant:o="card",className:d=""}){let c=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Logging Integrations"}),(0,t.jsx)(a.Tag,{color:"blue",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"space-y-3",children:e.map((e,l)=>{var n;let o=(n=e.callback_name,Object.entries(i.callback_map).find(([e,t])=>t===n)?.[0]||n),d=(0,r.resolveLogoSrc)(i.callbackInfo[o]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[d?(0,t.jsx)("img",{src:d,alt:o,className:"w-5 h-5 object-contain"}):(0,t.jsx)(s.CogIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-blue-800",children:o}),(0,t.jsxs)("span",{className:"block text-xs text-blue-600",children:[Object.keys(e.callback_vars).length," parameters configured"]})]})]}),(0,t.jsx)(a.Tag,{color:(e=>{switch(e){case"success":return"green";case"failure":return"red";case"success_and_failure":return"blue";default:return}})(e.callback_type),children:(e=>{switch(e){case"success":return"Success Only";case"failure":return"Failure Only";case"success_and_failure":return"Success & Failure";default:return e}})(e.callback_type)})]},l)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No logging integrations configured"})]})]}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-red-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Disabled Callbacks"}),(0,t.jsx)(a.Tag,{color:"red",children:n.length})]}),n.length>0?(0,t.jsx)("div",{className:"space-y-3",children:n.map((e,s)=>{let n=i.reverse_callback_map[e]||e,o=(0,r.resolveLogoSrc)(i.callbackInfo[n]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[o?(0,t.jsx)("img",{src:o,alt:n,className:"w-5 h-5 object-contain"}):(0,t.jsx)(l.BanIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-red-800",children:n}),(0,t.jsx)("span",{className:"block text-xs text-red-600",children:"Disabled for this key"})]})]}),(0,t.jsx)(a.Tag,{color:"red",children:"Disabled"})]},s)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No callbacks disabled"})]})]})]});return"card"===o?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${d}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-semibold text-gray-900",children:"Logging Settings"}),(0,t.jsx)("span",{className:"block text-xs text-gray-500",children:"Active logging integrations and disabled callbacks for this key"})]})}),c]}):(0,t.jsxs)("div",{className:`${d}`,children:[(0,t.jsx)("span",{className:"block font-medium text-gray-900 mb-3",children:"Logging Settings"}),c]})}])},183588,e=>{"use strict";var t=e.i(843476),a=e.i(266484);e.s(["default",0,({value:e,onChange:s,disabledCallbacks:l=[],onDisabledCallbacksChange:i})=>(0,t.jsx)(a.default,{value:e,onChange:s,disabledCallbacks:l,onDisabledCallbacksChange:i})])},214541,e=>{"use strict";var t=e.i(271645),a=e.i(135214),s=e.i(270345);e.s(["default",0,()=>{let[e,l]=(0,t.useState)([]),{accessToken:i,userId:r,userRole:n}=(0,a.default)();return(0,t.useEffect)(()=>{(async()=>{l(await (0,s.fetchTeams)(i,r,n,null))})()},[i,r,n]),{teams:e,setTeams:l}}])},784647,505022,721929,e=>{"use strict";var t=e.i(843476),a=e.i(464571),s=e.i(898586),l=e.i(592968),i=e.i(770914),r=e.i(312361),n=e.i(525720),o=e.i(282786),d=e.i(447566),c=e.i(772345),m=e.i(955135),u=e.i(646563),x=e.i(771674),p=e.i(72713),g=e.i(637235),h=e.i(962944);e.i(247167);var _=e.i(931067),j=e.i(271645);let y={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM810 654.3L512 886.5 214 654.3V226.7l298-101.6 298 101.6v427.6zm-405.8-201c-3-4.1-7.8-6.6-13-6.6H336c-6.5 0-10.3 7.4-6.5 12.7l126.4 174a16.1 16.1 0 0026 0l212.6-292.7c3.8-5.3 0-12.7-6.5-12.7h-55.2c-5.1 0-10 2.5-13 6.6L468.9 542.4l-64.7-89.1z"}}]},name:"safety-certificate",theme:"outlined"};var b=e.i(9583),f=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:y}))});let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M668.6 320c0-4.4-3.6-8-8-8h-54.5c-3 0-5.8 1.7-7.1 4.4l-84.7 168.8H511l-84.7-168.8a8 8 0 00-7.1-4.4h-55.7c-1.3 0-2.6.3-3.8 1-3.9 2.1-5.3 7-3.2 10.8l103.9 191.6h-57c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76v39h-76c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76V704c0 4.4 3.6 8 8 8h49.9c4.4 0 8-3.6 8-8v-63.5h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8h-76.3v-39h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8H564l103.7-191.6c.5-1.1.9-2.4.9-3.7zM157.9 504.2a352.7 352.7 0 01103.5-242.4c32.5-32.5 70.3-58.1 112.4-75.9 43.6-18.4 89.9-27.8 137.6-27.8 47.8 0 94.1 9.3 137.6 27.8 42.1 17.8 79.9 43.4 112.4 75.9 10 10 19.3 20.5 27.9 31.4l-50 39.1a8 8 0 003 14.1l156.8 38.3c5 1.2 9.9-2.6 9.9-7.7l.8-161.5c0-6.7-7.7-10.5-12.9-6.3l-47.8 37.4C770.7 146.3 648.6 82 511.5 82 277 82 86.3 270.1 82 503.8a8 8 0 008 8.2h60c4.3 0 7.8-3.5 7.9-7.8zM934 512h-60c-4.3 0-7.9 3.5-8 7.8a352.7 352.7 0 01-103.5 242.4 352.57 352.57 0 01-112.4 75.9c-43.6 18.4-89.9 27.8-137.6 27.8s-94.1-9.3-137.6-27.8a352.57 352.57 0 01-112.4-75.9c-10-10-19.3-20.5-27.9-31.4l49.9-39.1a8 8 0 00-3-14.1l-156.8-38.3c-5-1.2-9.9 2.6-9.9 7.7l-.8 161.7c0 6.7 7.7 10.5 12.9 6.3l47.8-37.4C253.3 877.7 375.4 942 512.5 942 747 942 937.7 753.9 942 520.2a8 8 0 00-8-8.2z"}}]},name:"transaction",theme:"outlined"};var k=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:v}))}),N={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"defs",attrs:{},children:[{tag:"style",attrs:{}}]},{tag:"path",attrs:{d:"M945 412H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h256c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM811 548H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h122c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM477.3 322.5H434c-6.2 0-11.2 5-11.2 11.2v248c0 3.6 1.7 6.9 4.6 9l148.9 108.6c5 3.6 12 2.6 15.6-2.4l25.7-35.1v-.1c3.6-5 2.5-12-2.5-15.6l-126.7-91.6V333.7c.1-6.2-5-11.2-11.1-11.2z"}},{tag:"path",attrs:{d:"M804.8 673.9H747c-5.6 0-10.9 2.9-13.9 7.7a321 321 0 01-44.5 55.7 317.17 317.17 0 01-101.3 68.3c-39.3 16.6-81 25-124 25-43.1 0-84.8-8.4-124-25-37.9-16-72-39-101.3-68.3s-52.3-63.4-68.3-101.3c-16.6-39.2-25-80.9-25-124 0-43.1 8.4-84.7 25-124 16-37.9 39-72 68.3-101.3 29.3-29.3 63.4-52.3 101.3-68.3 39.2-16.6 81-25 124-25 43.1 0 84.8 8.4 124 25 37.9 16 72 39 101.3 68.3a321 321 0 0144.5 55.7c3 4.8 8.3 7.7 13.9 7.7h57.8c6.9 0 11.3-7.2 8.2-13.3-65.2-129.7-197.4-214-345-215.7-216.1-2.7-395.6 174.2-396 390.1C71.6 727.5 246.9 903 463.2 903c149.5 0 283.9-84.6 349.8-215.8a9.18 9.18 0 00-8.2-13.3z"}}]},name:"field-time",theme:"outlined"},T=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:N}))}),w=e.i(304911);let{Text:S}=s.Typography;function C({label:e,value:a,icon:s,truncate:l=!1,copyable:r=!1,defaultUserIdCheck:n=!1}){let o=!a,d=n&&"default_user_id"===a,c=d?(0,t.jsx)(w.default,{userId:a}):(0,t.jsx)(S,{strong:!0,copyable:!!(r&&!o&&!d)&&{tooltips:[`Copy ${e}`,"Copied!"]},ellipsis:l,style:l?{maxWidth:160,display:"block"}:void 0,children:o?"-":a});return(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(S,{type:"secondary",children:s}),(0,t.jsx)(S,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:e})]}),(0,t.jsx)("div",{children:c})]})}let{Title:I,Text:A}=s.Typography;function F({userAlias:e,userEmail:a,userId:l}){let r=(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(A,{type:"secondary",children:(0,t.jsx)(x.UserOutlined,{})}),(0,t.jsx)(A,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:"User"})]});if(!e&&!a&&!l)return(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(A,{strong:!0,children:"-"})})]});let n="default_user_id"===l,d=e||a||l,c=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:e??null},{label:"User Email",value:a||null},{label:"User ID",value:l||null}].map(({label:e,value:a})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),a?(0,t.jsx)(s.Typography.Text,{className:"font-mono text-xs",style:{maxWidth:220},ellipsis:{tooltip:a},copyable:!0,children:a}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!n||e||a?(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)(A,{strong:!0,ellipsis:!0,style:{cursor:"default",maxWidth:200,display:"block"},children:d})})})]}):(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)(w.default,{userId:l})})})})]})}e.s(["KeyInfoHeader",0,function({data:e,onBack:s,onCreateNew:o,onRegenerate:x,onDelete:_,onResetSpend:j,canModifyKey:y=!0,backButtonText:b="Back to Keys",regenerateDisabled:v=!1,regenerateTooltip:N}){return(0,t.jsxs)("div",{children:[o&&(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"primary",icon:(0,t.jsx)(u.PlusOutlined,{}),onClick:o,children:"Create New Key"})}),(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"text",icon:(0,t.jsx)(d.ArrowLeftOutlined,{}),onClick:s,children:b})}),(0,t.jsxs)(n.Flex,{justify:"space-between",align:"start",style:{marginBottom:20},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(I,{level:3,copyable:{tooltips:["Copy Key Alias","Copied!"]},style:{margin:0},children:e.keyName}),(0,t.jsxs)(A,{type:"secondary",copyable:{text:e.keyId,tooltips:["Copy Key ID","Copied!"]},children:["Key ID: ",e.keyId]})]}),y&&(0,t.jsxs)(i.Space,{children:[(0,t.jsx)(l.Tooltip,{title:N||"",children:(0,t.jsx)("span",{children:(0,t.jsx)(a.Button,{icon:(0,t.jsx)(c.SyncOutlined,{}),onClick:x,disabled:v,children:"Regenerate Key"})})}),j&&(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(k,{}),onClick:j,children:"Reset Spend"}),(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(m.DeleteOutlined,{}),onClick:_,children:"Delete Key"})]})]}),(0,t.jsxs)(n.Flex,{align:"stretch",gap:40,style:{marginBottom:40},children:[(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(F,{userAlias:e.userAlias,userEmail:e.userEmail,userId:e.userId}),(0,t.jsx)(C,{label:"Expires",value:e.expires,icon:(0,t.jsx)(T,{})})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Created At",value:e.createdAt,icon:(0,t.jsx)(p.CalendarOutlined,{})}),(0,t.jsx)(C,{label:"Created By",value:e.createdBy,icon:(0,t.jsx)(f,{}),truncate:!0,copyable:!0,defaultUserIdCheck:!0})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Last Updated",value:e.lastUpdated,icon:(0,t.jsx)(g.ClockCircleOutlined,{})}),(0,t.jsx)(C,{label:"Last Active",value:e.lastActive,icon:(0,t.jsx)(h.ThunderboltOutlined,{})})]})]})]})}],784647);var M=e.i(599724),L=e.i(389083),R=e.i(278587);let E=j.forwardRef(function(e,t){return j.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),j.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["default",0,({autoRotate:e=!1,rotationInterval:a,lastRotationAt:s,keyRotationAt:l,nextRotationAt:i,variant:r="card",className:n=""})=>{let o=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},d=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)("div",{className:"space-y-3",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(R.RefreshIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(L.Badge,{color:e?"green":"gray",size:"xs",children:e?"Enabled":"Disabled"}),e&&a&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(M.Text,{className:"text-gray-400",children:"•"}),(0,t.jsxs)(M.Text,{className:"text-sm text-gray-600",children:["Every ",a]})]})]})}),(e||s||l||i)&&(0,t.jsxs)("div",{className:"space-y-3",children:[s&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Last Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(s)})]})]}),(l||i)&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Next Scheduled Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(i||l||"")})]})]}),e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"No rotation history available"})]})]}),!e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(R.RefreshIcon,{className:"w-4 h-4 text-gray-400"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"Auto-rotation is not enabled for this key"})]})]});return"card"===r?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${n}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(M.Text,{className:"text-xs text-gray-500",children:"Automatic key rotation settings and status for this key"})]})}),d]}):(0,t.jsxs)("div",{className:`${n}`,children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-900 mb-3",children:"Auto-Rotation"}),d]})}],505022);let P=["logging"];e.s(["extractLoggingSettings",0,e=>e&&"object"==typeof e&&Array.isArray(e.logging)?e.logging:[],"formatMetadataForDisplay",0,(e,t=2)=>JSON.stringify(e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([e])=>!P.includes(e))):{},null,t),"stripTagsFromMetadata",0,e=>{if(!e||"object"!=typeof e)return e;let{tags:t,...a}=e;return a}],721929)},65932,272753,e=>{"use strict";var t=e.i(954616),a=e.i(912598),s=e.i(602869),l=e.i(431703),i=e.i(135214),r=e.i(207082);let n=async(e,t)=>{let a=(0,s.getProxyBaseUrl)(),i=`${a?`${a}/key/${t}/reset_spend`:`/key/${t}/reset_spend`}`,r=await fetch(i,{method:"POST",headers:{[(0,s.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({reset_to:0})});if(!r.ok){let e=await r.json(),t=(0,l.deriveErrorMessage)(e);throw(0,s.handleError)(t),Error(t)}return r.json()};e.s(["useResetKeySpend",0,()=>{let{accessToken:e}=(0,i.default)(),s=(0,a.useQueryClient)();return(0,t.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return n(e,t)},onSuccess:()=>{s.invalidateQueries({queryKey:r.keyKeys.all})}})}],65932);var o=e.i(843476),d=e.i(492030),c=e.i(166406),m=e.i(772345),u=e.i(560445),x=e.i(464571),p=e.i(178654),g=e.i(525720),h=e.i(808613),_=e.i(311451),j=e.i(28651),y=e.i(212931),b=e.i(621192),f=e.i(770914),v=e.i(898586),k=e.i(271645),N=e.i(237016),T=e.i(727749),w=e.i(24529);let{Text:S}=v.Typography,C={pattern:/^(\d+(s|m|h|d|w|mo))?$/,message:"Must be a duration like 30s, 30m, 24h, 2d, 1w, or 1mo"};e.s(["RegenerateKeyModal",0,function({selectedToken:e,visible:t,onClose:a,onKeyUpdate:l}){let{accessToken:r}=(0,i.default)(),[n]=h.Form.useForm(),[v,I]=(0,k.useState)(null),[A,F]=(0,k.useState)(!1),[M,L]=(0,k.useState)(!1),R=(0,w.isKeyExpired)(e?.expires),E=h.Form.useWatch("duration",n),P=R?[{required:!0,message:"Expiration is required for expired keys"},C]:[C];(0,k.useEffect)(()=>{t&&e&&r&&n.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||"",grace_period:""})},[t,e,n,r]);let O=E?(0,w.calculateExpiryPreviewFromDuration)(E):null,B=async()=>{if(e&&r){F(!0);try{let t=await n.validateFields(),a=await (0,s.regenerateKeyCall)(r,e.token||e.token_id,t);I(a.key),T.default.success("Virtual Key regenerated successfully");let i={...a,token:a.token||a.key_id||e.token,key_name:a.key,max_budget:t.max_budget,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,expires:a.expires??e.expires};l&&l(i),F(!1)}catch(e){if(F(!1),e&&"object"==typeof e&&"errorFields"in e)return;console.error("Error regenerating key:",e),T.default.fromBackend(e)}}},D=()=>{I(null),F(!1),L(!1),n.resetFields(),a()};return(0,o.jsx)(y.Modal,{title:"Regenerate Virtual Key",open:t,onCancel:D,width:520,maskClosable:!1,footer:v?[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Close"}),(0,o.jsx)(N.CopyToClipboard,{text:v,onCopy:()=>{L(!0)},children:(0,o.jsx)(x.Button,{type:"primary",icon:M?(0,o.jsx)(d.CheckOutlined,{}):(0,o.jsx)(c.CopyOutlined,{}),children:M?"Copied":"Copy Key"})})]},"footer-actions")]:[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Cancel"}),(0,o.jsx)(x.Button,{type:"primary",icon:(0,o.jsx)(m.SyncOutlined,{}),onClick:B,loading:A,children:"Regenerate"})]},"footer-actions")],children:v?(0,o.jsxs)(g.Flex,{vertical:!0,gap:"middle",children:[(0,o.jsx)(u.Alert,{type:"warning",showIcon:!0,message:"Save it now, you will not see it again"}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Key Alias"}),(0,o.jsx)(S,{children:e?.key_alias||"No alias set"})]}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:6,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Virtual Key"}),(0,o.jsx)("div",{style:{background:"#f5f5f5",border:"1px solid #e8e8e8",borderRadius:6,padding:"14px 16px",fontFamily:"SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace",fontSize:16,wordBreak:"break-all",color:"#262626"},children:v})]})]}):(0,o.jsxs)(h.Form,{form:n,layout:"vertical",style:{marginTop:4},children:[(0,o.jsx)(h.Form.Item,{name:"key_alias",label:"Key Alias",children:(0,o.jsx)(_.Input,{disabled:!0})}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,o.jsx)(j.InputNumber,{step:.01,precision:2,style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})})]}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"duration",label:"Expire Key",rules:P,extra:(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsxs)(S,{type:R?"danger":"secondary",style:{fontSize:12},children:["Current expiry: ",e?.expires?(0,w.formatExpiresUtc)(e.expires):"Never",R&&" (expired)"]}),O&&(0,o.jsxs)(S,{type:"success",style:{fontSize:12},children:["New expiry: ",O]})]}),children:(0,o.jsx)(_.Input,{placeholder:"e.g. 30s, 30h, 30d"})})}),(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"grace_period",label:"Grace Period",tooltip:"Keep the old key valid for this duration after rotation. Both keys work during this period for seamless cutover. Empty = immediate revoke.",extra:(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Recommended: 24h to 72h for production keys"}),rules:[C],children:(0,o.jsx)(_.Input,{placeholder:"e.g. 24h, 2d"})})})]})]})})}],272753)},20147,e=>{"use strict";var t=e.i(843476),a=e.i(135214),s=e.i(510674),l=e.i(292639),i=e.i(214541),r=e.i(500330),n=e.i(11751),o=e.i(530212),d=e.i(389083),c=e.i(994388),m=e.i(304967),u=e.i(350967),x=e.i(197647),p=e.i(653824),g=e.i(881073),h=e.i(404206),_=e.i(723731),j=e.i(599724),y=e.i(629569),b=e.i(808613),f=e.i(212931),v=e.i(262218),k=e.i(784647),N=e.i(271645),T=e.i(708347),w=e.i(557662),S=e.i(505022),C=e.i(127952),I=e.i(721929),A=e.i(643449),F=e.i(727749),M=e.i(602869),L=e.i(65932),R=e.i(207082),E=e.i(912598),P=e.i(384767),O=e.i(272753),B=e.i(190702),D=e.i(891547),z=e.i(109799),K=e.i(921511),$=e.i(827252),U=e.i(779241),V=e.i(311451),W=e.i(199133),G=e.i(790848),q=e.i(592968),H=e.i(552130),J=e.i(9314),Q=e.i(392110),Y=e.i(844565),X=e.i(939510),Z=e.i(363256),ee=e.i(128233),et=e.i(319312),ea=e.i(833400),es=e.i(355619),el=e.i(75921),ei=e.i(234713),er=e.i(390605),en=e.i(702597),eo=e.i(435451),ed=e.i(183588),ec=e.i(916940);function em({keyData:e,onCancel:a,onSubmit:i,teams:r,accessToken:n,userID:o,userRole:d,premiumUser:m=!1}){let u=m||null!=d&&T.rolesWithWriteAccess.includes(d),[x]=b.Form.useForm(),[p,g]=(0,N.useState)([]),[h,_]=(0,N.useState)({}),j=r?.find(t=>t.team_id===e.team_id),[y,f]=(0,N.useState)([]),[v,k]=(0,N.useState)(Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[]),[S,C]=(0,N.useState)(e.organization_id||null),[A,L]=(0,N.useState)(e.auto_rotate||!1),[R,E]=(0,N.useState)(e.rotation_interval||""),[P,O]=(0,N.useState)(!e.expires),[B,eu]=(0,N.useState)(!1),[ex,ep]=(0,N.useState)(Array.isArray(e.budget_limits)?e.budget_limits:[]),[eg,eh]=(0,N.useState)((0,ea.tagLimitsToRows)(e.metadata?.tag_rpm_limit)),[e_,ej]=(0,N.useState)(e.budget_fallbacks&&"object"==typeof e.budget_fallbacks?e.budget_fallbacks:{}),{data:ey,isLoading:eb}=(0,z.useOrganizations)(),{data:ef}=(0,s.useProjects)(),{data:ev}=(0,l.useUISettings)(),ek=!!ev?.values?.enable_projects_ui,eN=!!e.project_id,eT=(()=>{if(!e.project_id)return null;let t=ef?.find(t=>t.project_id===e.project_id);return t?.project_alias?`${t.project_alias} (${e.project_id})`:e.project_id})();(0,N.useEffect)(()=>{let t=async()=>{if(o&&d&&n)try{if(null===e.team_id){let e=(await (0,M.modelAvailableCall)(n,o,d)).data.map(e=>e.id);f((0,es.excludeProxyWideSentinel)(e))}else if(j?.team_id){let e=await (0,en.fetchTeamModels)(o,d,n,j.team_id);f((0,es.excludeProxyWideSentinel)(Array.from(new Set([...j.models,...e]))))}}catch(e){console.error("Error fetching models:",e)}};(async()=>{if(n)try{let e=await (0,M.getPromptsList)(n);g(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}})(),t()},[o,d,n,j,e.team_id]),(0,N.useEffect)(()=>{x.setFieldValue("disabled_callbacks",v)},[x,v]);let ew=e=>e&&({"24h":"daily","7d":"weekly","30d":"monthly"})[e]||null,eS={...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},agents_and_groups:{agents:e.object_permission?.agents||[],accessGroups:e.object_permission?.agent_access_groups||[]},logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""};(0,N.useEffect)(()=>{x.setFieldsValue({...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""})},[e,x]),(0,N.useEffect)(()=>{x.setFieldValue("auto_rotate",A)},[A,x]),(0,N.useEffect)(()=>{R&&x.setFieldValue("rotation_interval",R)},[R,x]),(0,N.useEffect)(()=>{(async()=>{if(n)try{let e=await (0,M.tagListCall)(n);_(e)}catch(e){F.default.fromBackend("Error fetching tags: "+e)}})()},[n]);let eC=async t=>{try{if(eu(!0),"string"==typeof t.allowed_routes){let e=t.allowed_routes.trim();""===e?t.allowed_routes=[]:t.allowed_routes=e.split(",").map(e=>e.trim()).filter(e=>e.length>0)}let a=new Set(Array.isArray(e.allowed_routes)?e.allowed_routes:[]),s=new Set(Array.isArray(t.allowed_routes)?t.allowed_routes:[]);a.size===s.size&&[...s].every(e=>a.has(e))&&delete t.allowed_routes,P&&(t.duration=null);let l=ex.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);l.length>0?t.budget_limits=l:0===ex.length&&(t.budget_limits=[]);let{tag_rpm_limit:r}=(0,ea.tagRowsToLimits)(eg);t.tag_rpm_limit=r;let n=null!=e.budget_fallbacks&&Object.keys(e.budget_fallbacks).length>0;Object.keys(e_).length>0?t.budget_fallbacks=e_:n&&(t.budget_fallbacks={}),await i(t)}finally{eu(!1)}};return(0,t.jsxs)(b.Form,{form:x,onFinish:eC,initialValues:eS,layout:"vertical",children:[(0,t.jsx)(b.Form.Item,{label:"Key Alias",name:"key_alias",children:(0,t.jsx)(U.TextInput,{})}),(0,t.jsx)(b.Form.Item,{label:"Models",name:"models",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes||e.models!==t.models,children:({getFieldValue:a,setFieldValue:s})=>{let l=a("allowed_routes")||"",i="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[],r=i.includes("management_routes")||i.includes("info_routes"),n=a("models")||[];return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(W.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:r,value:r?[]:n,onChange:e=>{e.includes("all-team-models")?s("models",["all-team-models"]):e.includes("all-proxy-models")?s("models",["all-proxy-models"]):s("models",e)},children:[null!=e.team_id?null!=j&&(0,t.jsx)(W.Select.Option,{value:"all-team-models",children:"All Team Models"}):(0,t.jsx)(W.Select.Option,{value:"all-proxy-models",children:"All Proxy Models"}),y.map(e=>(0,t.jsx)(W.Select.Option,{value:e,disabled:(0,es.hasAllModelsSentinel)(n),children:e},e))]}),r&&(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Models field is disabled for this key type"})]})}})}),(0,t.jsx)(b.Form.Item,{label:"Key Type",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes,children:({getFieldValue:e,setFieldValue:a})=>{var s;let l=e("allowed_routes")||"",i=(s="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[])&&0!==s.length?s.includes("llm_api_routes")?"llm_api":s.includes("management_routes")?"management":s.includes("info_routes")?"read_only":"default":"default";return(0,t.jsxs)(W.Select,{placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",value:i,onChange:e=>{switch(e){case"default":a("allowed_routes","");break;case"llm_api":a("allowed_routes","llm_api_routes");break;case"management":a("allowed_routes","management_routes"),a("models",[])}},children:[(0,t.jsx)(W.Select.Option,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Full Access"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})}),(0,t.jsx)(W.Select.Option,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"AI APIs"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(W.Select.Option,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Management"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only management routes (user/team/key management)"})]})})]})}})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Routes"," ",(0,t.jsx)(q.Tooltip,{title:"List of allowed routes for the key (comma-separated). Can be specific routes (e.g., '/chat/completions') or route patterns (e.g., 'llm_api_routes', 'management_routes', '/keys/*'). Leave empty to allow all routes.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_routes",children:(0,t.jsx)(V.Input,{placeholder:"Enter allowed routes (comma-separated). Special values: llm_api_routes, management_routes. Examples: llm_api_routes, /chat/completions, /keys/*. Leave empty to allow all routes"})}),(0,t.jsx)(b.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(eo.default,{step:.01,style:{width:"100%"},placeholder:"Enter a numerical value"})}),(0,t.jsx)(b.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(W.Select,{placeholder:"n/a",children:[(0,t.jsx)(W.Select.Option,{value:"daily",children:"Daily"}),(0,t.jsx)(W.Select.Option,{value:"weekly",children:"Weekly"}),(0,t.jsx)(W.Select.Option,{value:"monthly",children:"Monthly"})]})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(q.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(et.BudgetWindowsEditor,{value:ex,onChange:ep})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(q.Tooltip,{title:"When a model exceeds its per-model budget, requests automatically reroute to fallback models instead of failing",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ee.BudgetFallbacksEditor,{value:e_,onChange:ej,availableModels:y})}),(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"tpm",name:"tpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"rpm",name:"rpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Throttle on budget exceeded"," ",(0,t.jsx)(q.Tooltip,{title:"When this key exceeds its max budget, throttle its TPM/RPM to the globally configured percentage instead of blocking access entirely. Requires budget_exceeded_throttle_percentage in litellm_settings and a TPM/RPM limit on the key.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"throttle_on_budget_exceeded",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:"Max Parallel Requests",name:"max_parallel_requests",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(b.Form.Item,{label:"Model TPM Limit",name:"model_tpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:"Model RPM Limit",name:"model_rpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Per-Tag Rate Limits"," ",(0,t.jsx)(q.Tooltip,{title:"Scope rate limits to a request tag so each tag (e.g. a cell or group) gets its own RPM counter. Requests without a matching tag fall back to the key-level limit.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ea.TagRateLimitEditor,{value:eg,onChange:eh})}),(0,t.jsx)(b.Form.Item,{label:"Guardrails",name:"guardrails",children:n&&(0,t.jsx)(D.default,{onChange:e=>{x.setFieldValue("guardrails",e)},accessToken:n,disabled:!u})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(q.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"disable_global_guardrails",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{disabled:!u,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(q.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"policies",children:n&&(0,t.jsx)(K.default,{onChange:e=>{x.setFieldValue("policies",e)},accessToken:n,disabled:!m})}),(0,t.jsx)(b.Form.Item,{label:"Tags",name:"tags",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",options:Object.values(h).map(e=>({value:e.name,label:e.name,title:e.description||e.name}))})}),(0,t.jsx)(b.Form.Item,{label:"Prompts",name:"prompts",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting prompts by key is a premium feature",placement:"top",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},disabled:!m,placeholder:m?Array.isArray(e.metadata?.prompts)&&e.metadata.prompts.length>0?`Current: ${e.metadata.prompts.join(", ")}`:"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:p.map(e=>({value:e,label:e}))})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(q.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",children:(0,t.jsx)(J.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:"Allowed Pass Through Routes",name:"allowed_passthrough_routes",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting allowed pass through routes by key is a premium feature",placement:"top",children:(0,t.jsx)(Y.default,{onChange:e=>x.setFieldValue("allowed_passthrough_routes",e),value:x.getFieldValue("allowed_passthrough_routes"),accessToken:n||"",placeholder:m?Array.isArray(e.metadata?.allowed_passthrough_routes)&&e.metadata.allowed_passthrough_routes.length>0?`Current: ${e.metadata.allowed_passthrough_routes.join(", ")}`:"Select or enter allowed pass through routes":"Premium feature - Upgrade to set allowed pass through routes by key",disabled:!m})})}),(0,t.jsx)(b.Form.Item,{label:"Vector Stores",name:"vector_stores",children:(0,t.jsx)(ec.default,{onChange:e=>x.setFieldValue("vector_stores",e),value:x.getFieldValue("vector_stores"),accessToken:n||"",placeholder:"Select vector stores"})}),(0,t.jsx)(b.Form.Item,{label:"MCP Servers / Access Groups",name:"mcp_servers_and_groups",children:(0,t.jsx)(el.default,{onChange:e=>x.setFieldValue("mcp_servers_and_groups",e),value:x.getFieldValue("mcp_servers_and_groups"),accessToken:n||"",placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(V.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.mcp_servers_and_groups!==t.mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(er.default,{accessToken:n||"",selectedServers:(x.getFieldValue("mcp_servers_and_groups")?.servers||[]).filter(e=>e!==ei.NO_MCP_SERVERS_SENTINEL),toolPermissions:x.getFieldValue("mcp_tool_permissions")||{},onChange:e=>x.setFieldsValue({mcp_tool_permissions:e})})})}),(0,t.jsx)(b.Form.Item,{label:"Agents / Access Groups",name:"agents_and_groups",children:(0,t.jsx)(H.default,{onChange:e=>x.setFieldValue("agents_and_groups",e),value:x.getFieldValue("agents_and_groups"),accessToken:n||"",placeholder:"Select agents or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(q.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",children:(0,t.jsx)(Z.default,{organizations:ey,loading:eb,disabled:"Admin"!==d,onChange:e=>{C(e||null),x.setFieldValue("team_id",void 0)}})}),(0,t.jsx)(b.Form.Item,{label:"Team ID",name:"team_id",help:ek&&eN?"Team is locked because this key belongs to a project":void 0,children:(0,t.jsx)(W.Select,{placeholder:"Select team",showSearch:!0,disabled:ek&&eN,style:{width:"100%"},onChange:e=>{let t=r?.find(t=>t.team_id===e)||null;t?.organization_id?(C(t.organization_id),x.setFieldValue("organization_id",t.organization_id)):e||(C(null),x.setFieldValue("organization_id",void 0))},filterOption:(e,t)=>{let a=S?r?.filter(e=>e.organization_id===S):r,s=a?.find(e=>e.team_id===t?.value);return!!s&&(s.team_alias?.toLowerCase().includes(e.toLowerCase())??!1)},children:(S?r?.filter(e=>e.organization_id===S):r)?.map(e=>(0,t.jsx)(W.Select.Option,{value:e.team_id,children:`${e.team_alias} (${e.team_id})`},e.team_id))})}),ek&&eN&&(0,t.jsx)(b.Form.Item,{label:"Project",children:(0,t.jsx)(V.Input,{value:eT??"",disabled:!0})}),(0,t.jsx)(b.Form.Item,{label:"Logging Settings",name:"logging_settings",children:(0,t.jsx)(ed.default,{value:x.getFieldValue("logging_settings"),onChange:e=>x.setFieldValue("logging_settings",e),disabledCallbacks:v,onDisabledCallbacksChange:e=>{k((0,w.mapInternalToDisplayNames)(e)),x.setFieldValue("disabled_callbacks",e)}})}),(0,t.jsx)(b.Form.Item,{label:"Metadata",name:"metadata",children:(0,t.jsx)(V.Input.TextArea,{rows:10})}),(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(Q.default,{form:x,autoRotationEnabled:A,onAutoRotationChange:L,rotationInterval:R,onRotationIntervalChange:E,neverExpire:P,onNeverExpireChange:O}),(0,t.jsx)(b.Form.Item,{name:"duration",hidden:!0,initialValue:"",children:(0,t.jsx)(V.Input,{})})]}),(0,t.jsx)(b.Form.Item,{name:"token",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"disabled_callbacks",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"auto_rotate",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"rotation_interval",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)("div",{className:"sticky z-10 bg-white p-4 border-t border-gray-200 -bottom-6 -inset-x-6",children:(0,t.jsxs)("div",{className:"flex justify-end items-center gap-2",children:[(0,t.jsx)(c.Button,{variant:"secondary",onClick:a,disabled:B,children:"Cancel"}),(0,t.jsx)(c.Button,{type:"submit",loading:B,children:"Save Changes"})]})})]})}let eu=["policies","guardrails","prompts","tags","allowed_passthrough_routes"],ex=e=>null==e||Array.isArray(e)&&0===e.length||"string"==typeof e&&""===e.trim();e.s(["default",0,function({onClose:e,keyData:D,teams:z,onKeyDataUpdate:K,onDelete:$,backButtonText:U="Back to Keys"}){let V,{accessToken:W,userId:G,userRole:q,premiumUser:H}=(0,a.default)(),J=(0,E.useQueryClient)(),Q=H||null!=q&&T.rolesWithWriteAccess.includes(q),{teams:Y}=(0,i.default)(),{data:X}=(0,s.useProjects)(),{data:Z}=(0,l.useUISettings)(),ee=!!Z?.values?.enable_projects_ui,[et,ea]=(0,N.useState)(!1),[es]=b.Form.useForm(),[el,ei]=(0,N.useState)(!1),[er,en]=(0,N.useState)(!1),[eo,ed]=(0,N.useState)(""),[ec,ep]=(0,N.useState)(!1),[eg,eh]=(0,N.useState)(!1),{mutate:e_,isPending:ej}=(0,L.useResetKeySpend)(),[ey,eb]=(0,N.useState)(D),[ef,ev]=(0,N.useState)(null),[ek,eN]=(0,N.useState)(!1),[eT,ew]=(0,N.useState)({}),[eS,eC]=(0,N.useState)(!1);if((0,N.useEffect)(()=>{D&&eb(D)},[D]),(0,N.useEffect)(()=>{(async()=>{let e=ey?.metadata?.policies;if(!W||!e||!Array.isArray(e)||0===e.length)return;eC(!0);let t={};try{await Promise.all(e.map(async e=>{try{let a=await (0,M.getPolicyInfoWithGuardrails)(W,e);t[e]=a.resolved_guardrails||[]}catch(a){console.error(`Failed to fetch guardrails for policy ${e}:`,a),t[e]=[]}})),ew(t)}catch(e){console.error("Failed to fetch policy guardrails:",e)}finally{eC(!1)}})()},[W,ey?.metadata?.policies]),(0,N.useEffect)(()=>{if(ek){let e=setTimeout(()=>{eN(!1)},5e3);return()=>clearTimeout(e)}},[ek]),!ey)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)(c.Button,{icon:o.ArrowLeftIcon,variant:"light",onClick:e,className:"mb-4",children:U}),(0,t.jsx)(j.Text,{children:"Key not found"})]});let eI=async e=>{try{if(!W)return;let t=e.token;for(let a of(e.key=t,Q||(delete e.guardrails,delete e.prompts),eu)){let t=ey.metadata?.[a]??ey[a];ex(e[a])&&ex(t)&&delete e[a]}let a=!!ey.metadata?.disable_global_guardrails;if(!!e.disable_global_guardrails===a&&delete e.disable_global_guardrails,e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),void 0!==e.vector_stores&&(e.object_permission={...ey.object_permission,vector_stores:e.vector_stores||[]},delete e.vector_stores),void 0!==e.mcp_servers_and_groups){let{servers:t,accessGroups:a,toolsets:s}=e.mcp_servers_and_groups||{servers:[],accessGroups:[],toolsets:[]};e.object_permission={...ey.object_permission,mcp_servers:t||[],mcp_access_groups:a||[],mcp_toolsets:s||[]},delete e.mcp_servers_and_groups}if(void 0!==e.mcp_tool_permissions){let t=e.mcp_tool_permissions||{};Object.keys(t).length>0&&(e.object_permission={...e.object_permission,mcp_tool_permissions:t}),delete e.mcp_tool_permissions}if(void 0!==e.agents_and_groups){let{agents:t,accessGroups:a}=e.agents_and_groups||{agents:[],accessGroups:[]};e.object_permission={...e.object_permission,agents:t||[],agent_access_groups:a||[]},delete e.agents_and_groups}if(e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),e.tpm_limit=(0,n.mapEmptyStringToNull)(e.tpm_limit),e.rpm_limit=(0,n.mapEmptyStringToNull)(e.rpm_limit),e.max_parallel_requests=(0,n.mapEmptyStringToNull)(e.max_parallel_requests),e.metadata&&"string"==typeof e.metadata)try{let t=JSON.parse(e.metadata);"tags"in t&&delete t.tags,e.metadata={...t,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}catch(e){console.error("Error parsing metadata JSON:",e),F.default.error("Invalid metadata JSON");return}else{let{tags:t,...a}=e.metadata||{};e.metadata={...a,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}"tags"in e&&delete e.tags,delete e.logging_settings,e.budget_duration&&(e.budget_duration=({daily:"24h",weekly:"7d",monthly:"30d"})[e.budget_duration]);let s=await (0,M.keyUpdateCall)(W,e);eb(e=>e?{...e,...s}:void 0),K&&K(s),F.default.success("Key updated successfully"),ea(!1)}catch(e){F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error updating key:",e)}},eA=async()=>{try{if(en(!0),!W)return;await (0,M.keyDeleteCall)(W,ey.token||ey.token_id),F.default.success("Key deleted successfully"),await J.invalidateQueries({queryKey:R.keyKeys.lists()}),$&&$(),e()}catch(e){console.error("Error deleting the key:",e),F.default.fromBackend(e)}finally{en(!1),ei(!1),ed("")}},eF=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},eM=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||"")||G===ey.user_id&&"Internal Viewer"!==q,eL=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||""),eR=ey.team_id?Y?.find(e=>e.team_id===ey.team_id):null,eE=null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:eR?.max_budget!=null?`$${(0,r.formatNumberWithCommas)(eR.max_budget,2)} (Team: ${eR.team_alias||eR.team_id}${eR.budget_duration?` / ${eR.budget_duration}`:""})`:"Unlimited";return(0,t.jsxs)("div",{className:"w-full h-full overflow-y-auto p-4",children:[(0,t.jsx)(k.KeyInfoHeader,{data:{keyName:ey.key_alias||"Virtual Key",keyId:ey.token_id||ey.token,userId:ey.user_id||"",userEmail:ey.user_email||"",userAlias:ey.user?.user_alias??null,createdBy:ey.created_by_user?.user_alias||ey.created_by_user?.user_email||ey.created_by||"",createdAt:ey.created_at?eF(ey.created_at):"",lastUpdated:ey.updated_at?eF(ey.updated_at):"",lastActive:ey.last_active?eF(ey.last_active):"Never",expires:ey.expires?eF(ey.expires):"Never"},onBack:e,onRegenerate:()=>ep(!0),onDelete:()=>ei(!0),onResetSpend:eL?()=>eh(!0):void 0,canModifyKey:eM,backButtonText:U,regenerateDisabled:!H,regenerateTooltip:H?void 0:"This is a LiteLLM Enterprise feature, and requires a valid key to use."}),(0,t.jsx)(O.RegenerateKeyModal,{selectedToken:ey,visible:ec,onClose:()=>ep(!1),onKeyUpdate:e=>{eb(t=>{if(t)return{...t,...e,created_at:new Date().toLocaleString()}}),ev(new Date),eN(!0),K&&K({...e,created_at:new Date().toLocaleString()})}}),(0,t.jsx)(C.default,{isOpen:el,title:"Delete Key",alertMessage:"This action is irreversible and will immediately revoke access for any applications using this key.",message:"Are you sure you want to delete this Virtual Key?",resourceInformationTitle:"Key Information",resourceInformation:[{label:"Key Alias",value:ey?.key_alias||"-"},{label:"Key ID",value:ey?.token_id||ey?.token||"-",code:!0},{label:"Team ID",value:ey?.team_id||"-",code:!0},{label:"Spend",value:ey?.spend?`$${(0,r.formatNumberWithCommas)(ey.spend,4)}`:"$0.0000"}],onCancel:()=>{ei(!1),ed("")},onOk:eA,confirmLoading:er,requiredConfirmation:ey?.key_alias}),(0,t.jsxs)(f.Modal,{title:"Reset Key Spend",open:eg,onOk:()=>{e_(ey.token||ey.token_id,{onSuccess:()=>{eb(e=>e?{...e,spend:0}:void 0),K&&K({spend:0}),F.default.success("Key spend reset to $0"),eh(!1)},onError:e=>{F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error resetting key spend:",e)}})},onCancel:()=>eh(!1),okText:"Reset",okButtonProps:{danger:!0},confirmLoading:ej,children:[(0,t.jsxs)("p",{children:["Reset spend for ",(0,t.jsx)("strong",{children:ey?.key_alias||ey?.token_id||"this key"})," to"," ",(0,t.jsx)("strong",{children:"$0"}),"?"]}),(0,t.jsxs)("p",{style:{color:"#666",fontSize:"0.875rem",marginTop:8},children:["Current spend: ",(0,t.jsxs)("strong",{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),". Spend history is preserved in logs. This resets the current period spend counter, the same as an automatic budget reset."]})]}),(0,t.jsxs)(p.TabGroup,{children:[(0,t.jsxs)(g.TabList,{className:"mb-4",children:[(0,t.jsx)(x.Tab,{children:"Overview"}),(0,t.jsx)(x.Tab,{children:"Settings"})]}),(0,t.jsxs)(_.TabPanels,{children:[(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(u.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Spend"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(y.Title,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),(0,t.jsxs)(j.Text,{children:["of ",eE]})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Rate Limits"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),!!ey.metadata?.throttle_on_budget_exceeded&&(0,t.jsx)(j.Text,{children:"Throttle on budget exceeded: Yes"})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Models"}),(0,t.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)(d.Badge,{color:"red",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsx)(m.Card,{children:(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",accessToken:W})}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Guardrails"}),Array.isArray(ey.metadata?.guardrails)&&ey.metadata.guardrails.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:ey.metadata.guardrails.map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",children:e},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No guardrails configured"}),"boolean"==typeof ey.metadata?.disable_global_guardrails&&!0===ey.metadata.disable_global_guardrails&&(0,t.jsx)("div",{className:"mt-3 pt-3 border-t border-gray-200",children:(0,t.jsx)(d.Badge,{color:"yellow",children:"Global Guardrails Disabled"})})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Policies"}),Array.isArray(ey.metadata?.policies)&&ey.metadata.policies.length>0?(0,t.jsx)("div",{className:"space-y-4",children:ey.metadata.policies.map((e,a)=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(d.Badge,{color:"purple",children:e}),eS&&(0,t.jsx)(j.Text,{className:"text-xs text-gray-400",children:"Loading guardrails..."})]}),!eS&&eT[e]&&eT[e].length>0&&(0,t.jsxs)("div",{className:"ml-4 pl-3 border-l-2 border-gray-200",children:[(0,t.jsx)(j.Text,{className:"text-xs text-gray-500 mb-1",children:"Resolved Guardrails:"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:eT[e].map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",size:"xs",children:e},a))})]})]},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No policies configured"})]}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"card"}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"card"})]})}),(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(m.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(y.Title,{children:"Key Settings"}),!et&&eM&&(0,t.jsx)(c.Button,{onClick:()=>ea(!0),children:"Edit Settings"})]}),et?(0,t.jsx)(em,{keyData:ey,onCancel:()=>ea(!1),onSubmit:eI,teams:z,accessToken:W,userID:G,userRole:q,premiumUser:H}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key ID"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.token_id||ey.token})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key Alias"}),(0,t.jsx)(j.Text,{children:ey.key_alias||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Secret Key"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.key_name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Team ID"}),(0,t.jsx)(j.Text,{children:ey.team_id||"Not Set"})]}),ee&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Project"}),(0,t.jsx)(j.Text,{children:ey.project_id?(V=X?.find(e=>e.project_id===ey.project_id),V?.project_alias?`${V.project_alias} (${ey.project_id})`:ey.project_id):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Organization"}),(0,t.jsx)(j.Text,{children:(ey.organization_id??ey.org_id)||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Created"}),(0,t.jsx)(j.Text,{children:eF(ey.created_at)})]}),ef&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Last Regenerated"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.Text,{children:eF(ef)}),(0,t.jsx)(d.Badge,{color:"green",size:"xs",children:"Recent"})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Expires"}),(0,t.jsx)(j.Text,{children:ey.expires?eF(ey.expires):"Never"})]}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"inline",className:"pt-4 border-t border-gray-200"}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Spend"}),(0,t.jsxs)(j.Text,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)," USD"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget"}),(0,t.jsx)(j.Text,{children:null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:"Unlimited"})]}),ey.budget_fallbacks&&Object.keys(ey.budget_fallbacks).length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget Fallbacks"}),(0,t.jsx)("div",{className:"mt-1 space-y-1",children:Object.entries(ey.budget_fallbacks).map(([e,a])=>(0,t.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,t.jsx)("span",{className:"font-medium",children:e}),(0,t.jsx)("span",{className:"mx-1 text-gray-400",children:"->"}),a.join(", ")]},e))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Tags"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.metadata?.tags)&&ey.metadata.tags.length>0?ey.metadata.tags.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No tags specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Prompts"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.prompts)&&ey.metadata.prompts.length>0?ey.metadata.prompts.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No prompts specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Routes"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.allowed_routes)&&ey.allowed_routes.length>0?ey.allowed_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(v.Tag,{color:"green",children:"All routes allowed"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Pass Through Routes"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.allowed_passthrough_routes)&&ey.metadata.allowed_passthrough_routes.length>0?ey.metadata.allowed_passthrough_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No pass through routes specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Disable Global Guardrails"}),(0,t.jsx)(j.Text,{children:ey.metadata?.disable_global_guardrails===!0?(0,t.jsx)(d.Badge,{color:"yellow",children:"Enabled - Global guardrails bypassed"}):(0,t.jsx)(d.Badge,{color:"green",children:"Disabled - Global guardrails active"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Rate Limits"}),(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Max Parallel Requests:"," ",null!==ey.max_parallel_requests?ey.max_parallel_requests:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model TPM Limits:"," ",ey.metadata?.model_tpm_limit?JSON.stringify(ey.metadata.model_tpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model RPM Limits:"," ",ey.metadata?.model_rpm_limit?JSON.stringify(ey.metadata.model_rpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Tag RPM Limits:"," ",ey.metadata?.tag_rpm_limit&&Object.keys(ey.metadata.tag_rpm_limit).length>0?JSON.stringify(ey.metadata.tag_rpm_limit):"Unlimited"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Metadata"}),(0,t.jsx)("pre",{className:"bg-gray-100 p-2 rounded-sm text-xs overflow-auto mt-1",children:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(ey.metadata))})]}),(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",className:"pt-4 border-t border-gray-200",accessToken:W}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"inline",className:"pt-4 border-t border-gray-200"})]})]})})]})]})]})}],20147)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02ax9y70kcggv.js b/litellm/proxy/_experimental/out/_next/static/chunks/02ax9y70kcggv.js deleted file mode 100644 index c436d5dcf0f..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/02ax9y70kcggv.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,246349,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,t])},195116,e=>{"use strict";let t=(0,e.i(475254).default)("wrench",[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",key:"cbrjhi"}]]);e.s(["Wrench",0,t],195116)},180127,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);e.s(["default",0,t])},434166,e=>{"use strict";e.s(["getSecureItem",0,function(e){try{let t=window.sessionStorage.getItem(e);if(null===t)return null;return decodeURIComponent(atob(t).split("").map(e=>"%"+e.charCodeAt(0).toString(16).padStart(2,"0")).join(""))}catch{return null}},"setSecureItem",0,function(e,t){window.sessionStorage.setItem(e,btoa(encodeURIComponent(t).replace(/%([0-9A-F]{2})/g,(e,t)=>String.fromCharCode(parseInt(t,16)))))}])},531278,e=>{"use strict";let t=(0,e.i(475254).default)("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);e.s(["Loader2",0,t],531278)},302747,e=>{"use strict";var t=e.i(843476),r=e.i(115504);e.s(["Skeleton",0,function({className:e,...s}){return(0,t.jsx)("div",{"data-slot":"skeleton",className:(0,r.cn)("animate-pulse rounded-md bg-accent",e),...s})}])},463059,e=>{"use strict";var t=e.i(246349);e.s(["ChevronRight",()=>t.default])},269638,e=>{"use strict";let t=(0,e.i(475254).default)("circle-check-big",[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]]);e.s(["CheckCircle",0,t],269638)},292335,122520,165615,779129,280024,e=>{"use strict";let t={NONE:"none",API_KEY:"api_key",BEARER_TOKEN:"bearer_token",TOKEN:"token",BASIC:"basic",OAUTH2:"oauth2",AWS_SIGV4:"aws_sigv4"},r="client_credentials",s={SSE:"sse",HTTP:"http",STDIO:"stdio",OPENAPI:"openapi"};e.s(["AUTH_TYPE",0,t,"MCP_OAUTH2_FLOW_M2M",0,r,"OAUTH_FLOW",0,{INTERACTIVE:"interactive",M2M:"m2m"},"TRANSPORT",0,s,"getMcpOAuthMode",0,function(e){return e.auth_type!==t.OAUTH2?null:e.oauth2_flow===r?"m2m":e.delegate_auth_to_upstream?"passthrough":"obo"},"handleAuth",0,e=>null==e?t.NONE:e,"handleTransport",0,(e,t)=>null==e?s.SSE:t&&e!==s.STDIO?s.OPENAPI:e],292335);var a=e.i(271645),n=e.i(602869),i=e.i(727749);function l(e){if(e instanceof Error)return e.message;if(e&&"object"==typeof e){let t=e.detail;return"string"==typeof t?t:Array.isArray(t)?t.map(e=>e&&"object"==typeof e?"string"==typeof e.msg?e.msg:JSON.stringify(e):String(e)).join("; "):t&&"object"==typeof t&&"string"==typeof t.error?t.error:"string"==typeof e.message?e.message:JSON.stringify(e)}return String(e)}e.s(["extractErrorMessage",0,l],122520);let o=e=>{let t=new Uint8Array(e),r="";return t.forEach(e=>r+=String.fromCharCode(e)),btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")},c=()=>{let e=new Uint8Array(32);return window.crypto.getRandomValues(e),o(e.buffer)},d=async e=>{let t=new TextEncoder().encode(e);return o(await window.crypto.subtle.digest("SHA-256",t))};e.s(["generateCodeChallenge",0,d,"generateCodeVerifier",0,c],165615);var u=e.i(434166);let f=()=>{{let e=window.location.pathname||"",t=e.indexOf("/ui"),r=t>=0?e.slice(0,t+3).replace(/\/+$/,""):"";return`${window.location.origin}${r}/mcp/oauth/callback`}},h=(...e)=>{e.forEach(e=>{try{window.sessionStorage.removeItem(e)}catch(e){}})};e.s(["TOOLS_OAUTH_UI_STATE_KEY",0,"litellm-mcp-oauth-tools-state","buildCallbackUrl",0,f,"clearStorage",0,h],779129);let p="litellm-user-mcp-oauth-flow-state",m="litellm-user-mcp-oauth-result",x=(e,t)=>{(0,u.setSecureItem)(e,t)},g=e=>(0,u.getSecureItem)(e);e.s(["useUserMcpOAuthFlow",0,({accessToken:e,serverId:t,serverAlias:r,scopes:s,clientId:o,onSuccess:u})=>{let[v,b]=(0,a.useState)("idle"),[y,w]=(0,a.useState)(null),j=(0,a.useRef)(!1),N=(0,a.useCallback)(async()=>{try{let a;b("authorizing"),w(null);let i=o??void 0;if(!i)try{let s=await (0,n.registerMcpOAuthClient)(e,t,{client_name:r||t,grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"});i=s?.client_id,a=s?.client_secret}catch(e){}let l=c(),u=await d(l),h=crypto.randomUUID(),m=f(),g=s?.filter(e=>e.trim()).join(" "),v=(0,n.buildMcpOAuthAuthorizeUrl)({serverId:t,clientId:i,redirectUri:m,state:h,codeChallenge:u,scope:g}),y={state:h,codeVerifier:l,serverId:t,redirectUri:m,clientId:i,clientSecret:a,scopes:s};x(p,JSON.stringify(y));let j=new URL(window.location.href);j.searchParams.set("mcpOauthReturn","apps"),x("litellm-mcp-oauth-return-url",j.toString()),window.location.href=v}catch(t){let e=l(t);w(e),b("error"),i.default.error(e)}},[e,t,r,s,o]),k=(0,a.useCallback)(async()=>{if(j.current)return;let r=g(m);if(!r)return;let s=g(p);if(!s)return;try{let e=JSON.parse(s);if(e.serverId&&e.serverId!==t)return}catch(e){}j.current=!0,h(m);let a=null,o=null;try{a=JSON.parse(r);let e=g(p);o=e?JSON.parse(e):null}catch(e){w("Failed to resume OAuth flow. Please retry."),b("error"),j.current=!1,h(p);return}try{if(!o?.state||!o.codeVerifier||!o.serverId)throw Error("OAuth session state was lost. Please retry.");if(!a?.state||a.state!==o.state)throw Error("OAuth state mismatch. Please retry.");if(a.error)throw Error(a.error_description||a.error);if(!a.code)throw Error("Authorization code missing in callback.");b("exchanging");let t=await (0,n.exchangeMcpOAuthToken)({serverId:o.serverId,code:a.code,clientId:o.clientId,clientSecret:o.clientSecret,codeVerifier:o.codeVerifier,redirectUri:o.redirectUri,accessToken:e});await (0,n.storeMCPOAuthUserCredential)(e,o.serverId,{access_token:t.access_token,refresh_token:t.refresh_token,expires_in:t.expires_in,scopes:o.scopes}),b("success"),w(null),i.default.success("Connected successfully"),u()}catch(t){let e=l(t);w(e),b("error"),i.default.error(e)}finally{h(p),setTimeout(()=>{j.current=!1},1e3)}},[e,t,u]);return(0,a.useEffect)(()=>{k()},[k]),{startOAuthFlow:N,status:v,error:y}}],280024)},248536,e=>{"use strict";var t=e.i(843476),r=e.i(271645),s=e.i(618566),a=e.i(405033),n=e.i(266027),i=e.i(555436),l=e.i(180127),l=l,o=e.i(463059),c=e.i(195116),d=e.i(269638),u=e.i(531278),f=e.i(519455),h=e.i(793479),p=e.i(302747),m=e.i(981140),x=e.i(30030),g=e.i(820783),v=e.i(991918),b=new WeakMap;function y(e,t){var r,s;let a,n,i;if("at"in Array.prototype)return Array.prototype.at.call(e,t);let l=(r=e,s=t,a=r.length,(i=(n=w(s))>=0?n:a+n)<0||i>=a?-1:i);return -1===l?void 0:e[l]}function w(e){return e!=e||0===e?0:Math.trunc(e)}(class e extends Map{#e;constructor(e){super(e),this.#e=[...super.keys()],b.set(this,!0)}set(e,t){return b.get(this)&&(this.has(e)?this.#e[this.#e.indexOf(e)]=e:this.#e.push(e)),super.set(e,t),this}insert(e,t,r){let s,a=this.has(t),n=this.#e.length,i=w(e),l=i>=0?i:n+i,o=l<0||l>=n?-1:l;if(o===this.size||a&&o===this.size-1||-1===o)return this.set(t,r),this;let c=this.size+ +!a;i<0&&l++;let d=[...this.#e],u=!1;for(let e=l;e=this.size&&(s=this.size-1),this.at(s)}keyFrom(e,t){let r=this.indexOf(e);if(-1===r)return;let s=r+t;return s<0&&(s=0),s>=this.size&&(s=this.size-1),this.keyAt(s)}find(e,t){let r=0;for(let s of this){if(Reflect.apply(e,t,[s,r,this]))return s;r++}}findIndex(e,t){let r=0;for(let s of this){if(Reflect.apply(e,t,[s,r,this]))return r;r++}return -1}filter(t,r){let s=[],a=0;for(let e of this)Reflect.apply(t,r,[e,a,this])&&s.push(e),a++;return new e(s)}map(t,r){let s=[],a=0;for(let e of this)s.push([e[0],Reflect.apply(t,r,[e,a,this])]),a++;return new e(s)}reduce(...e){let[t,r]=e,s=0,a=r??this.at(0);for(let r of this)a=0===s&&1===e.length?r:Reflect.apply(t,this,[a,r,s,this]),s++;return a}reduceRight(...e){let[t,r]=e,s=r??this.at(-1);for(let r=this.size-1;r>=0;r--){let a=this.at(r);s=r===this.size-1&&1===e.length?a:Reflect.apply(t,this,[s,a,r,this])}return s}toSorted(t){return new e([...this.entries()].sort(t))}toReversed(){let t=new e;for(let e=this.size-1;e>=0;e--){let r=this.keyAt(e),s=this.get(r);t.set(r,s)}return t}toSpliced(...t){let r=[...this.entries()];return r.splice(...t),new e(r)}slice(t,r){let s=new e,a=this.size-1;if(void 0===t)return s;t<0&&(t+=this.size),void 0!==r&&r>0&&(a=r-1);for(let e=t;e<=a;e++){let t=this.keyAt(e),r=this.get(t);s.set(t,r)}return s}every(e,t){let r=0;for(let s of this){if(!Reflect.apply(e,t,[s,r,this]))return!1;r++}return!0}some(e,t){let r=0;for(let s of this){if(Reflect.apply(e,t,[s,r,this]))return!0;r++}return!1}});var j=e.i(610772),N=e.i(248425),k=e.i(30207),S=e.i(369340),C=e.i(586318),A="rovingFocusGroup.onEntryFocus",_={bubbles:!1,cancelable:!0},T="RovingFocusGroup",[R,E,O]=function(e){let s=e+"CollectionProvider",[a,n]=(0,x.createContextScope)(s),[i,l]=a(s,{collectionRef:{current:null},itemMap:new Map}),o=e=>{let{scope:s,children:a}=e,n=r.useRef(null),l=r.useRef(new Map).current;return(0,t.jsx)(i,{scope:s,itemMap:l,collectionRef:n,children:a})};o.displayName=s;let c=e+"CollectionSlot",d=(0,v.createSlot)(c),u=r.forwardRef((e,r)=>{let{scope:s,children:a}=e,n=l(c,s),i=(0,g.useComposedRefs)(r,n.collectionRef);return(0,t.jsx)(d,{ref:i,children:a})});u.displayName=c;let f=e+"CollectionItemSlot",h="data-radix-collection-item",p=(0,v.createSlot)(f),m=r.forwardRef((e,s)=>{let{scope:a,children:n,...i}=e,o=r.useRef(null),c=(0,g.useComposedRefs)(s,o),d=l(f,a);return r.useEffect(()=>(d.itemMap.set(o,{ref:o,...i}),()=>void d.itemMap.delete(o))),(0,t.jsx)(p,{...{[h]:""},ref:c,children:n})});return m.displayName=f,[{Provider:o,Slot:u,ItemSlot:m},function(t){let s=l(e+"CollectionConsumer",t);return r.useCallback(()=>{let e=s.collectionRef.current;if(!e)return[];let t=Array.from(e.querySelectorAll(`[${h}]`));return Array.from(s.itemMap.values()).sort((e,r)=>t.indexOf(e.ref.current)-t.indexOf(r.ref.current))},[s.collectionRef,s.itemMap])},n]}(T),[I,M]=(0,x.createContextScope)(T,[O]),[P,U]=I(T),z=r.forwardRef((e,r)=>(0,t.jsx)(R.Provider,{scope:e.__scopeRovingFocusGroup,children:(0,t.jsx)(R.Slot,{scope:e.__scopeRovingFocusGroup,children:(0,t.jsx)(F,{...e,ref:r})})}));z.displayName=T;var F=r.forwardRef((e,s)=>{let{__scopeRovingFocusGroup:a,orientation:n,loop:i=!1,dir:l,currentTabStopId:o,defaultCurrentTabStopId:c,onCurrentTabStopIdChange:d,onEntryFocus:u,preventScrollOnEntryFocus:f=!1,...h}=e,p=r.useRef(null),x=(0,g.useComposedRefs)(s,p),v=(0,C.useDirection)(l),[b,y]=(0,S.useControllableState)({prop:o,defaultProp:c??null,onChange:d,caller:T}),[w,j]=r.useState(!1),R=(0,k.useCallbackRef)(u),O=E(a),I=r.useRef(!1),[M,U]=r.useState(0);return r.useEffect(()=>{let e=p.current;if(e)return e.addEventListener(A,R),()=>e.removeEventListener(A,R)},[R]),(0,t.jsx)(P,{scope:a,orientation:n,dir:v,loop:i,currentTabStopId:b,onItemFocus:r.useCallback(e=>y(e),[y]),onItemShiftTab:r.useCallback(()=>j(!0),[]),onFocusableItemAdd:r.useCallback(()=>U(e=>e+1),[]),onFocusableItemRemove:r.useCallback(()=>U(e=>e-1),[]),children:(0,t.jsx)(N.Primitive.div,{tabIndex:w||0===M?-1:0,"data-orientation":n,...h,ref:x,style:{outline:"none",...e.style},onMouseDown:(0,m.composeEventHandlers)(e.onMouseDown,()=>{I.current=!0}),onFocus:(0,m.composeEventHandlers)(e.onFocus,e=>{let t=!I.current;if(e.target===e.currentTarget&&t&&!w){let t=new CustomEvent(A,_);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){let e=O().filter(e=>e.focusable);$([e.find(e=>e.active),e.find(e=>e.id===b),...e].filter(Boolean).map(e=>e.ref.current),f)}}I.current=!1}),onBlur:(0,m.composeEventHandlers)(e.onBlur,()=>j(!1))})})}),L="RovingFocusGroupItem",D=r.forwardRef((e,s)=>{let{__scopeRovingFocusGroup:a,focusable:n=!0,active:i=!1,tabStopId:l,children:o,...c}=e,d=(0,j.useId)(),u=l||d,f=U(L,a),h=f.currentTabStopId===u,p=E(a),{onFocusableItemAdd:x,onFocusableItemRemove:g,currentTabStopId:v}=f;return r.useEffect(()=>{if(n)return x(),()=>g()},[n,x,g]),(0,t.jsx)(R.ItemSlot,{scope:a,id:u,focusable:n,active:i,children:(0,t.jsx)(N.Primitive.span,{tabIndex:h?0:-1,"data-orientation":f.orientation,...c,ref:s,onMouseDown:(0,m.composeEventHandlers)(e.onMouseDown,e=>{n?f.onItemFocus(u):e.preventDefault()}),onFocus:(0,m.composeEventHandlers)(e.onFocus,()=>f.onItemFocus(u)),onKeyDown:(0,m.composeEventHandlers)(e.onKeyDown,e=>{if("Tab"===e.key&&e.shiftKey)return void f.onItemShiftTab();if(e.target!==e.currentTarget)return;let t=function(e,t,r){var s;let a=(s=e.key,"rtl"!==r?s:"ArrowLeft"===s?"ArrowRight":"ArrowRight"===s?"ArrowLeft":s);if(!("vertical"===t&&["ArrowLeft","ArrowRight"].includes(a))&&!("horizontal"===t&&["ArrowUp","ArrowDown"].includes(a)))return H[a]}(e,f.orientation,f.dir);if(void 0!==t){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let a=p().filter(e=>e.focusable).map(e=>e.ref.current);if("last"===t)a.reverse();else if("prev"===t||"next"===t){var r,s;"prev"===t&&a.reverse();let n=a.indexOf(e.currentTarget);a=f.loop?(r=a,s=n+1,r.map((e,t)=>r[(s+t)%r.length])):a.slice(n+1)}setTimeout(()=>$(a))}}),children:"function"==typeof o?o({isCurrentTabStop:h,hasTabStop:null!=v}):o})})});D.displayName=L;var H={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function $(e,t=!1){let r=document.activeElement;for(let s of e)if(s===r||(s.focus({preventScroll:t}),document.activeElement!==r))return}var K=e.i(296626),B="Tabs",[V,W]=(0,x.createContextScope)(B,[M]),G=M(),[J,Y]=V(B),q=r.forwardRef((e,r)=>{let{__scopeTabs:s,value:a,onValueChange:n,defaultValue:i,orientation:l="horizontal",dir:o,activationMode:c="automatic",...d}=e,u=(0,C.useDirection)(o),[f,h]=(0,S.useControllableState)({prop:a,onChange:n,defaultProp:i??"",caller:B});return(0,t.jsx)(J,{scope:s,baseId:(0,j.useId)(),value:f,onValueChange:h,orientation:l,dir:u,activationMode:c,children:(0,t.jsx)(N.Primitive.div,{dir:u,"data-orientation":l,...d,ref:r})})});q.displayName=B;var Q="TabsList",X=r.forwardRef((e,r)=>{let{__scopeTabs:s,loop:a=!0,...n}=e,i=Y(Q,s),l=G(s);return(0,t.jsx)(z,{asChild:!0,...l,orientation:i.orientation,dir:i.dir,loop:a,children:(0,t.jsx)(N.Primitive.div,{role:"tablist","aria-orientation":i.orientation,...n,ref:r})})});X.displayName=Q;var Z="TabsTrigger",ee=r.forwardRef((e,r)=>{let{__scopeTabs:s,value:a,disabled:n=!1,...i}=e,l=Y(Z,s),o=G(s),c=es(l.baseId,a),d=ea(l.baseId,a),u=a===l.value;return(0,t.jsx)(D,{asChild:!0,...o,focusable:!n,active:u,children:(0,t.jsx)(N.Primitive.button,{type:"button",role:"tab","aria-selected":u,"aria-controls":d,"data-state":u?"active":"inactive","data-disabled":n?"":void 0,disabled:n,id:c,...i,ref:r,onMouseDown:(0,m.composeEventHandlers)(e.onMouseDown,e=>{n||0!==e.button||!1!==e.ctrlKey?e.preventDefault():l.onValueChange(a)}),onKeyDown:(0,m.composeEventHandlers)(e.onKeyDown,e=>{[" ","Enter"].includes(e.key)&&l.onValueChange(a)}),onFocus:(0,m.composeEventHandlers)(e.onFocus,()=>{let e="manual"!==l.activationMode;u||n||!e||l.onValueChange(a)})})})});ee.displayName=Z;var et="TabsContent",er=r.forwardRef((e,s)=>{let{__scopeTabs:a,value:n,forceMount:i,children:l,...o}=e,c=Y(et,a),d=es(c.baseId,n),u=ea(c.baseId,n),f=n===c.value,h=r.useRef(f);return r.useEffect(()=>{let e=requestAnimationFrame(()=>h.current=!1);return()=>cancelAnimationFrame(e)},[]),(0,t.jsx)(K.Presence,{present:i||f,children:({present:r})=>(0,t.jsx)(N.Primitive.div,{"data-state":f?"active":"inactive","data-orientation":c.orientation,role:"tabpanel","aria-labelledby":d,hidden:!r,id:u,tabIndex:0,...o,ref:s,style:{...e.style,animationDuration:h.current?"0s":void 0},children:r&&l})})});function es(e,t){return`${e}-trigger-${t}`}function ea(e,t){return`${e}-content-${t}`}er.displayName=et,e.s(["Content",0,er,"List",0,X,"Root",0,q,"Tabs",0,q,"TabsContent",0,er,"TabsList",0,X,"TabsTrigger",0,ee,"Trigger",0,ee,"createTabsScope",0,W],926209);var en=e.i(926209),en=en,ei=e.i(115504);function el({className:e,orientation:r="horizontal",...s}){return(0,t.jsx)(en.Root,{"data-slot":"tabs","data-orientation":r,orientation:r,className:(0,ei.cn)("group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",e),...s})}let eo=(0,ei.cva)({base:"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",variants:{variant:{default:"bg-muted",line:"gap-1 bg-transparent"}},defaultVariants:{variant:"default"}});function ec({className:e,variant:r="default",...s}){return(0,t.jsx)(en.List,{"data-slot":"tabs-list","data-variant":r,className:(0,ei.cn)(eo({variant:r}),e),...s})}function ed({className:e,...r}){return(0,t.jsx)(en.Trigger,{"data-slot":"tabs-trigger",className:(0,ei.cn)("relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4","group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent","data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground","after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",e),...r})}var eu=e.i(602869),ef=e.i(292335),eh=e.i(888259),ep=e.i(280024);let em=({server:e,accessToken:s,onConnect:a,variant:n="badge"})=>{let i=e.server_name??e.alias??e.server_id,{startOAuthFlow:l,status:o}=(0,ep.useUserMcpOAuthFlow)({accessToken:s,serverId:e.server_id,serverAlias:i,onSuccess:(0,r.useCallback)(()=>a(e.server_id),[a,e.server_id])}),c="authorizing"===o||"exchanging"===o;return"button"===n?(0,t.jsxs)(f.Button,{onClick:l,disabled:c,className:"font-semibold h-[38px] min-w-[110px]",children:[c&&(0,t.jsx)(u.Loader2,{className:"h-4 w-4 animate-spin mr-1.5"}),c?"Connecting…":"Connect"]}):(0,t.jsx)("span",{onClick:e=>{e.stopPropagation(),c||l()},className:`text-[11px] font-semibold rounded-md px-2 py-0.5 shrink-0 whitespace-nowrap ${c?"text-muted-foreground bg-muted cursor-default":"text-primary-foreground bg-primary cursor-pointer hover:bg-primary/90"}`,children:c?"Connecting…":"Connect"})},ex=["#1677ff","#52c41a","#fa8c16","#eb2f96","#722ed1","#13c2c2","#fa541c","#2f54eb","#a0d911","#faad14"];function eg(e){let t=0;for(let r=0;r{let[m,x]=(0,r.useState)([]),[g,v]=(0,r.useState)(!0),[b,y]=(0,r.useState)(""),[w,j]=(0,r.useState)("all"),[N,k]=(0,r.useState)(new Set),[S,C]=(0,r.useState)(null),[A,_]=(0,r.useState)({}),[T,R]=(0,r.useState)(!1),[E,O]=(0,r.useState)(new Set),I=(0,r.useRef)([]);(0,r.useEffect)(()=>{I.current=m},[m]);let M=(0,r.useRef)(s);(0,r.useEffect)(()=>{M.current=s},[s]);let P=(0,r.useRef)(a);(0,r.useEffect)(()=>{P.current=a},[a]);let U=e=>e.server_name??e.alias??e.server_id,z=(0,r.useRef)(!1),F=(0,r.useCallback)(async t=>{try{let r=await (0,eu.listMCPTools)(e,t.server_id);if(z.current)return;let s=Array.isArray(r?.tools)?r.tools:[];_(e=>({...e,[U(t)]:s.length}))}catch{}},[e]),L=(0,r.useCallback)(async t=>{try{let r=await (0,eu.getMCPOAuthUserCredentialStatus)(e,t.server_id);if(z.current)return;r.has_credential&&!r.is_expired&&O(e=>new Set(e).add(t.server_id))}catch{}},[e]);(0,r.useEffect)(()=>(z.current=!1,(0,eu.fetchMCPServers)(e).then(async e=>{if(z.current)return;let t=Array.isArray(e)?e:e?.data??[];for(let e of(x(t),v(!1),R(!0),Array.from({length:Math.ceil(t.length/5)},(e,r)=>t.slice(5*r,(r+1)*5)))){if(z.current)return;await Promise.allSettled(e.map(e=>F(e)))}z.current||R(!1),t.filter(e=>e.auth_type===ef.AUTH_TYPE.OAUTH2).forEach(e=>L(e))}).catch(()=>{z.current||(x([]),v(!1))}),()=>{z.current=!0}),[e,F,L]),(0,r.useEffect)(()=>{if(0===E.size)return;let e=I.current.filter(e=>E.has(e.server_id)&&!M.current.includes(U(e))).map(U);e.length>0&&P.current([...M.current,...e])},[E]);let D=async(t,r,n)=>{if(!r){a(s.filter(e=>e!==t)),n&&O(e=>{let t=new Set(e);return t.delete(n),t});return}k(e=>new Set(e).add(t));try{let r=n??t,s=await (0,eu.listMCPTools)(e,r);if(s?.error)return void eh.default.warning(`Could not load tools for ${t}`);M.current.includes(t)||a([...M.current,t])}catch{eh.default.warning(`Could not load tools for ${t}`)}finally{k(e=>{let r=new Set(e);return r.delete(t),r})}},{data:H,isLoading:$}=(0,n.useQuery)({queryKey:["mcp-apps-panel-detail-tools",S?.server_id],queryFn:()=>(0,eu.listMCPTools)(e,S.server_id),enabled:!!S}),K=Array.isArray(H?.tools)?H.tools:[],B=m.filter(e=>{let t=U(e),r=!b.trim()||t.toLowerCase().includes(b.toLowerCase())||(e.description??"").toLowerCase().includes(b.toLowerCase()),a="all"===w||s.includes(t);return r&&a}),V=m.filter(e=>s.includes(U(e))).length,W=Object.values(A).reduce((e,t)=>e+t,0);if(S){let r=U(S),a=s.includes(r),n=N.has(r),i=eg(r);return(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)(f.Button,{variant:"ghost",size:"sm",onClick:()=>C(null),className:"-ml-3 mb-5 gap-1.5 text-muted-foreground hover:text-foreground",children:[(0,t.jsx)(l.default,{className:"h-3 w-3"}),"Back"]}),(0,t.jsxs)("div",{className:"flex items-start gap-5 mb-7",children:[S.mcp_info?.logo_url?(0,t.jsx)("img",{src:S.mcp_info.logo_url,alt:`${r} logo`,className:"w-16 h-16 rounded-2xl object-contain shrink-0 bg-muted/50",onError:e=>{let t=e.target;t.style.display="none",t.nextElementSibling&&(t.nextElementSibling.style.display="flex")}}):null,(0,t.jsx)("div",{className:"w-16 h-16 rounded-2xl flex items-center justify-center text-white font-bold text-[28px] shrink-0",style:{background:i,display:S.mcp_info?.logo_url?"none":"flex"},children:r.charAt(0).toUpperCase()}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("h2",{className:"m-0 mb-1 text-[22px] font-bold text-foreground",children:r}),(0,t.jsx)("p",{className:"m-0 text-sm text-muted-foreground",children:S.description??"MCP server"})]}),S.auth_type===ef.AUTH_TYPE.OAUTH2?E.has(S.server_id)?(0,t.jsx)(f.Button,{variant:"destructive",onClick:async()=>{try{await (0,eu.deleteMCPOAuthUserCredential)(e,S.server_id)}catch(e){}O(e=>{let t=new Set(e);return t.delete(S.server_id),t}),P.current(M.current.filter(e=>e!==r))},className:"font-semibold h-[38px] min-w-[110px]",children:"Disconnect"}):(0,t.jsx)(em,{server:S,accessToken:e,onConnect:e=>{O(t=>new Set(t).add(e))},variant:"button"}):(0,t.jsxs)(f.Button,{variant:a?"outline":"default",disabled:n,onClick:()=>D(r,!a,S.server_id),className:"font-semibold h-[38px] min-w-[110px]",children:[n&&(0,t.jsx)(u.Loader2,{className:"h-4 w-4 animate-spin mr-1.5"}),a?"Disconnect":"Connect"]})]}),(0,t.jsx)("h3",{className:"m-0 mb-3 text-[15px] font-semibold text-foreground",children:"Information"}),(0,t.jsx)("div",{className:"border rounded-lg overflow-hidden mb-7",children:[["Server ID",S.server_id],["Transport",(0,ef.handleTransport)(S.transport,S.spec_path)],["Status",a?"Connected":"Not connected"]].filter(([,e])=>e).map(([e,r],s,a)=>(0,t.jsxs)("div",{className:`flex px-4 py-3 text-[13px] ${s(0,t.jsxs)("div",{className:"border rounded-lg px-3.5 py-2.5 bg-muted/30 flex flex-col gap-1.5",children:[(0,t.jsx)(p.Skeleton,{className:"h-3.5 w-1/3"}),(0,t.jsx)(p.Skeleton,{className:"h-3 w-2/3"})]},r))}):0===K.length?(0,t.jsx)("div",{className:"text-muted-foreground text-[13px] py-2",children:"No tools available"}):(0,t.jsx)("div",{className:"flex flex-col gap-2",children:K.map(e=>(0,t.jsxs)("div",{className:"border rounded-lg px-3.5 py-2.5 bg-muted/30",children:[(0,t.jsxs)("div",{className:`flex items-center gap-2 ${e.description?"mb-1":""}`,children:[(0,t.jsx)(c.Wrench,{className:"h-3.5 w-3.5 text-muted-foreground"}),(0,t.jsx)("span",{className:"text-[13px] font-semibold text-foreground font-mono",children:e.name})]}),e.description&&(0,t.jsx)("p",{className:"m-0 text-xs text-muted-foreground pl-[21px]",children:e.description})]},e.name))})]})}return(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-5 gap-4 flex-wrap",children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-1",children:[(0,t.jsx)("h2",{className:"m-0 text-lg font-semibold text-foreground",children:"MCP Servers"}),(0,t.jsx)("span",{className:"text-[10px] font-semibold text-primary bg-primary/10 rounded px-1.5 py-0.5 uppercase tracking-wider",children:"Beta"})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("p",{className:"m-0 text-[13px] text-muted-foreground",children:"Browse tools, authenticate once, use in chat"}),T?(0,t.jsxs)("span",{className:"flex items-center gap-1.5 text-xs text-muted-foreground",children:[(0,t.jsx)(u.Loader2,{className:"h-3 w-3 animate-spin"}),"Loading tools..."]}):W>0?(0,t.jsxs)("span",{className:"flex items-center gap-1 text-xs text-muted-foreground",children:[(0,t.jsx)(c.Wrench,{className:"h-3 w-3"}),W," tool",1!==W?"s":""," available"]}):null]})]}),(0,t.jsxs)("div",{className:"relative w-[220px]",children:[(0,t.jsx)(i.Search,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground"}),(0,t.jsx)(h.Input,{placeholder:"Search servers...",value:b,onChange:e=>y(e.target.value),className:"pl-9 text-[13px] h-9"})]})]}),(0,t.jsx)(el,{value:w,onValueChange:e=>j(e),className:"mb-4",children:(0,t.jsxs)(ec,{variant:"line",className:"border-b rounded-none w-full justify-start h-auto p-0",children:[(0,t.jsx)(ed,{value:"all",className:"rounded-none px-4 py-2 text-[13px]",children:"All"}),(0,t.jsxs)(ed,{value:"connected",className:"rounded-none px-4 py-2 text-[13px]",children:["Connected",V>0?` (${V})`:""]})]})}),g?(0,t.jsx)("div",{className:"grid grid-cols-2 border rounded-lg overflow-hidden",children:Array.from({length:6},(e,r)=>(0,t.jsxs)("div",{className:`flex items-center gap-3 p-4 ${r%2==0?"border-r":""} ${r<4?"border-b":""}`,children:[(0,t.jsx)(p.Skeleton,{className:"w-[38px] h-[38px] rounded-xl shrink-0"}),(0,t.jsxs)("div",{className:"flex-1 min-w-0 flex flex-col gap-1.5",children:[(0,t.jsx)(p.Skeleton,{className:"h-3.5 w-2/3"}),(0,t.jsx)(p.Skeleton,{className:"h-3 w-1/2"})]})]},r))}):0===B.length?(0,t.jsx)("div",{className:"text-center text-muted-foreground text-[13px] py-12 px-3",children:0===m.length?"No MCP servers configured. Add servers in Tools -> MCP Servers.":"connected"===w?"No servers connected yet.":"No servers match your search."}):(0,t.jsx)("div",{className:"grid grid-cols-2 border rounded-lg overflow-hidden",children:B.map((r,a)=>{let n=U(r),i=s.includes(n),l=eg(n),u=A[n];return(0,t.jsxs)("div",{onClick:()=>C(r),className:`flex items-center gap-3 p-4 bg-card cursor-pointer transition-colors hover:bg-accent/30 min-w-0 ${a%2==0?"border-r":""} ${Math.floor(a/2){let t=e.target;t.style.display="none",t.nextElementSibling&&(t.nextElementSibling.style.display="flex")}}):null,(0,t.jsx)("div",{className:"w-[38px] h-[38px] rounded-xl flex items-center justify-center text-white font-bold text-base shrink-0",style:{background:l,display:r.mcp_info?.logo_url?"none":"flex"},children:n.charAt(0).toUpperCase()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("div",{className:"text-sm font-medium text-foreground truncate",children:n}),(0,t.jsxs)("div",{className:"text-xs text-muted-foreground mt-0.5 flex items-center gap-1.5",children:[(0,t.jsx)("span",{className:"truncate",children:r.description??"MCP server"}),void 0!==u?u>0?(0,t.jsxs)("span",{className:"shrink-0 flex items-center gap-1 text-muted-foreground",children:["· ",(0,t.jsx)(c.Wrench,{className:"h-2.5 w-2.5"})," ",u]}):null:T?(0,t.jsx)(p.Skeleton,{className:"w-7 h-3 shrink-0"}):null]})]}),r.auth_type===ef.AUTH_TYPE.OAUTH2?E.has(r.server_id)?(0,t.jsx)(d.CheckCircle,{className:"h-3.5 w-3.5 text-emerald-600 shrink-0"}):(0,t.jsx)(em,{server:r,accessToken:e,onConnect:e=>{O(t=>new Set(t).add(e))},variant:"badge"}):i?(0,t.jsx)("span",{className:"w-[7px] h-[7px] rounded-full bg-emerald-600 dark:bg-emerald-400 shrink-0"}):null,(0,t.jsx)(o.ChevronRight,{className:"h-3 w-3 text-muted-foreground/40 shrink-0"})]},r.server_id)})})]})};function eb(){let{accessToken:e,selectedMCPServers:n,setSelectedMCPServers:i}=(0,a.useChatShell)(),l=(0,s.useRouter)(),o=(0,s.useSearchParams)().get("mcpOauthReturn");return(0,r.useEffect)(()=>{if(o){let e=new URL(window.location.href);e.searchParams.delete("mcpOauthReturn"),l.replace(e.pathname+e.search)}},[o,l]),(0,t.jsx)("div",{className:"flex-1 min-h-0 overflow-auto w-full py-8 px-8",children:(0,t.jsx)(ev,{accessToken:e,selectedServers:n,onChange:i})})}e.s(["default",0,function(){return(0,t.jsx)(r.Suspense,{children:(0,t.jsx)(eb,{})})}],248536)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02c1-r_khzb89.js b/litellm/proxy/_experimental/out/_next/static/chunks/02c1-r_khzb89.js deleted file mode 100644 index 48e8446d60e..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/02c1-r_khzb89.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},362024,e=>{"use strict";var t=e.i(988122);e.s(["Collapse",()=>t.default])},91979,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"}}]},name:"reload",theme:"outlined"};var l=e.i(9583),o=a.forwardRef(function(e,o){return a.createElement(l.default,(0,t.default)({},e,{ref:o,icon:n}))});e.s(["ReloadOutlined",0,o],91979)},608856,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),n=e.i(209428),l=e.i(392221),o=e.i(951160),r=e.i(174428),s=t.createContext(null),i=t.createContext({}),d=e.i(211577),c=e.i(931067),u=e.i(361275),f=e.i(404948),p=e.i(244009),m=e.i(703923),h=e.i(611935),x=["prefixCls","className","containerRef"];let g=function(e){var n=e.prefixCls,l=e.className,o=e.containerRef,r=(0,m.default)(e,x),s=t.useContext(i).panel,d=(0,h.useComposeRef)(s,o);return t.createElement("div",(0,c.default)({className:(0,a.default)("".concat(n,"-content"),l),role:"dialog",ref:d},(0,p.default)(e,{aria:!0}),{"aria-modal":"true"},r))};var y=e.i(883110);function b(e){return"string"==typeof e&&String(Number(e))===e?((0,y.default)(!1,"Invalid value type of `width` or `height` which should be number type instead."),Number(e)):e}var v={width:0,height:0,overflow:"hidden",outline:"none",position:"absolute"},w=t.forwardRef(function(e,o){var r,i,m,h=e.prefixCls,x=e.open,y=e.placement,w=e.inline,j=e.push,k=e.forceRender,S=e.autoFocus,C=e.keyboard,$=e.classNames,O=e.rootClassName,E=e.rootStyle,z=e.zIndex,_=e.className,N=e.id,I=e.style,R=e.motion,D=e.width,T=e.height,M=e.children,B=e.mask,F=e.maskClosable,P=e.maskMotion,L=e.maskClassName,W=e.maskStyle,A=e.afterOpenChange,K=e.onClose,H=e.onMouseEnter,U=e.onMouseOver,q=e.onMouseLeave,X=e.onClick,Y=e.onKeyDown,J=e.onKeyUp,G=e.styles,V=e.drawerRender,Z=t.useRef(),Q=t.useRef(),ee=t.useRef();t.useImperativeHandle(o,function(){return Z.current}),t.useEffect(function(){if(x&&S){var e;null==(e=Z.current)||e.focus({preventScroll:!0})}},[x]);var et=t.useState(!1),ea=(0,l.default)(et,2),en=ea[0],el=ea[1],eo=t.useContext(s),er=null!=(r=null!=(i=null==(m="boolean"==typeof j?j?{}:{distance:0}:j||{})?void 0:m.distance)?i:null==eo?void 0:eo.pushDistance)?r:180,es=t.useMemo(function(){return{pushDistance:er,push:function(){el(!0)},pull:function(){el(!1)}}},[er]);t.useEffect(function(){var e,t;x?null==eo||null==(e=eo.push)||e.call(eo):null==eo||null==(t=eo.pull)||t.call(eo)},[x]),t.useEffect(function(){return function(){var e;null==eo||null==(e=eo.pull)||e.call(eo)}},[]);var ei=t.createElement(u.default,(0,c.default)({key:"mask"},P,{visible:B&&x}),function(e,l){var o=e.className,r=e.style;return t.createElement("div",{className:(0,a.default)("".concat(h,"-mask"),o,null==$?void 0:$.mask,L),style:(0,n.default)((0,n.default)((0,n.default)({},r),W),null==G?void 0:G.mask),onClick:F&&x?K:void 0,ref:l})}),ed="function"==typeof R?R(y):R,ec={};if(en&&er)switch(y){case"top":ec.transform="translateY(".concat(er,"px)");break;case"bottom":ec.transform="translateY(".concat(-er,"px)");break;case"left":ec.transform="translateX(".concat(er,"px)");break;default:ec.transform="translateX(".concat(-er,"px)")}"left"===y||"right"===y?ec.width=b(D):ec.height=b(T);var eu={onMouseEnter:H,onMouseOver:U,onMouseLeave:q,onClick:X,onKeyDown:Y,onKeyUp:J},ef=t.createElement(u.default,(0,c.default)({key:"panel"},ed,{visible:x,forceRender:k,onVisibleChanged:function(e){null==A||A(e)},removeOnLeave:!1,leavedClassName:"".concat(h,"-content-wrapper-hidden")}),function(l,o){var r=l.className,s=l.style,i=t.createElement(g,(0,c.default)({id:N,containerRef:o,prefixCls:h,className:(0,a.default)(_,null==$?void 0:$.content),style:(0,n.default)((0,n.default)({},I),null==G?void 0:G.content)},(0,p.default)(e,{aria:!0}),eu),M);return t.createElement("div",(0,c.default)({className:(0,a.default)("".concat(h,"-content-wrapper"),null==$?void 0:$.wrapper,r),style:(0,n.default)((0,n.default)((0,n.default)({},ec),s),null==G?void 0:G.wrapper)},(0,p.default)(e,{data:!0})),V?V(i):i)}),ep=(0,n.default)({},E);return z&&(ep.zIndex=z),t.createElement(s.Provider,{value:es},t.createElement("div",{className:(0,a.default)(h,"".concat(h,"-").concat(y),O,(0,d.default)((0,d.default)({},"".concat(h,"-open"),x),"".concat(h,"-inline"),w)),style:ep,tabIndex:-1,ref:Z,onKeyDown:function(e){var t,a,n=e.keyCode,l=e.shiftKey;switch(n){case f.default.TAB:n===f.default.TAB&&(l||document.activeElement!==ee.current?l&&document.activeElement===Q.current&&(null==(a=ee.current)||a.focus({preventScroll:!0})):null==(t=Q.current)||t.focus({preventScroll:!0}));break;case f.default.ESC:K&&C&&(e.stopPropagation(),K(e))}}},ei,t.createElement("div",{tabIndex:0,ref:Q,style:v,"aria-hidden":"true","data-sentinel":"start"}),ef,t.createElement("div",{tabIndex:0,ref:ee,style:v,"aria-hidden":"true","data-sentinel":"end"})))});let j=function(e){var a=e.open,s=e.prefixCls,d=e.placement,c=e.autoFocus,u=e.keyboard,f=e.width,p=e.mask,m=void 0===p||p,h=e.maskClosable,x=e.getContainer,g=e.forceRender,y=e.afterOpenChange,b=e.destroyOnClose,v=e.onMouseEnter,j=e.onMouseOver,k=e.onMouseLeave,S=e.onClick,C=e.onKeyDown,$=e.onKeyUp,O=e.panelRef,E=t.useState(!1),z=(0,l.default)(E,2),_=z[0],N=z[1],I=t.useState(!1),R=(0,l.default)(I,2),D=R[0],T=R[1];(0,r.default)(function(){T(!0)},[]);var M=!!D&&void 0!==a&&a,B=t.useRef(),F=t.useRef();(0,r.default)(function(){M&&(F.current=document.activeElement)},[M]);var P=t.useMemo(function(){return{panel:O}},[O]);if(!g&&!_&&!M&&b)return null;var L=(0,n.default)((0,n.default)({},e),{},{open:M,prefixCls:void 0===s?"rc-drawer":s,placement:void 0===d?"right":d,autoFocus:void 0===c||c,keyboard:void 0===u||u,width:void 0===f?378:f,mask:m,maskClosable:void 0===h||h,inline:!1===x,afterOpenChange:function(e){var t,a;N(e),null==y||y(e),e||!F.current||null!=(t=B.current)&&t.contains(F.current)||null==(a=F.current)||a.focus({preventScroll:!0})},ref:B},{onMouseEnter:v,onMouseOver:j,onMouseLeave:k,onClick:S,onKeyDown:C,onKeyUp:$});return t.createElement(i.Provider,{value:P},t.createElement(o.default,{open:M||g||_,autoDestroy:!1,getContainer:x,autoLock:m&&(M||_)},t.createElement(w,L)))};var k=e.i(981444),S=e.i(617206),C=e.i(122767),$=e.i(613541),O=e.i(340010),E=e.i(242064),z=e.i(922611),_=e.i(563113),N=e.i(185793);let I=e=>{var n,l,o,r;let s,{prefixCls:i,ariaId:d,title:c,footer:u,extra:f,closable:p,loading:m,onClose:h,headerStyle:x,bodyStyle:g,footerStyle:y,children:b,classNames:v,styles:w}=e,j=(0,E.useComponentConfig)("drawer");s=!1===p?void 0:void 0===p||!0===p?"start":(null==p?void 0:p.placement)==="end"?"end":"start";let k=t.useCallback(e=>t.createElement("button",{type:"button",onClick:h,className:(0,a.default)(`${i}-close`,{[`${i}-close-${s}`]:"end"===s})},e),[h,i,s]),[S,C]=(0,_.useClosable)((0,_.pickClosable)(e),(0,_.pickClosable)(j),{closable:!0,closeIconRender:k});return t.createElement(t.Fragment,null,c||S?t.createElement("div",{style:Object.assign(Object.assign(Object.assign({},null==(o=j.styles)?void 0:o.header),x),null==w?void 0:w.header),className:(0,a.default)(`${i}-header`,{[`${i}-header-close-only`]:S&&!c&&!f},null==(r=j.classNames)?void 0:r.header,null==v?void 0:v.header)},t.createElement("div",{className:`${i}-header-title`},"start"===s&&C,c&&t.createElement("div",{className:`${i}-title`,id:d},c)),f&&t.createElement("div",{className:`${i}-extra`},f),"end"===s&&C):null,t.createElement("div",{className:(0,a.default)(`${i}-body`,null==v?void 0:v.body,null==(n=j.classNames)?void 0:n.body),style:Object.assign(Object.assign(Object.assign({},null==(l=j.styles)?void 0:l.body),g),null==w?void 0:w.body)},m?t.createElement(N.default,{active:!0,title:!1,paragraph:{rows:5},className:`${i}-body-skeleton`}):b),(()=>{var e,n;if(!u)return null;let l=`${i}-footer`;return t.createElement("div",{className:(0,a.default)(l,null==(e=j.classNames)?void 0:e.footer,null==v?void 0:v.footer),style:Object.assign(Object.assign(Object.assign({},null==(n=j.styles)?void 0:n.footer),y),null==w?void 0:w.footer)},u)})())};e.i(296059);var R=e.i(915654),D=e.i(183293),T=e.i(246422),M=e.i(838378);let B=(e,t)=>({"&-enter, &-appear":Object.assign(Object.assign({},e),{"&-active":t}),"&-leave":Object.assign(Object.assign({},t),{"&-active":e})}),F=(e,t)=>Object.assign({"&-enter, &-appear, &-leave":{"&-start":{transition:"none"},"&-active":{transition:`all ${t}`}}},B({opacity:e},{opacity:1})),P=(0,T.genStyleHooks)("Drawer",e=>{let t=(0,M.mergeToken)(e,{});return[(e=>{let{borderRadiusSM:t,componentCls:a,zIndexPopup:n,colorBgMask:l,colorBgElevated:o,motionDurationSlow:r,motionDurationMid:s,paddingXS:i,padding:d,paddingLG:c,fontSizeLG:u,lineHeightLG:f,lineWidth:p,lineType:m,colorSplit:h,marginXS:x,colorIcon:g,colorIconHover:y,colorBgTextHover:b,colorBgTextActive:v,colorText:w,fontWeightStrong:j,footerPaddingBlock:k,footerPaddingInline:S,calc:C}=e,$=`${a}-content-wrapper`;return{[a]:{position:"fixed",inset:0,zIndex:n,pointerEvents:"none",color:w,"&-pure":{position:"relative",background:o,display:"flex",flexDirection:"column",[`&${a}-left`]:{boxShadow:e.boxShadowDrawerLeft},[`&${a}-right`]:{boxShadow:e.boxShadowDrawerRight},[`&${a}-top`]:{boxShadow:e.boxShadowDrawerUp},[`&${a}-bottom`]:{boxShadow:e.boxShadowDrawerDown}},"&-inline":{position:"absolute"},[`${a}-mask`]:{position:"absolute",inset:0,zIndex:n,background:l,pointerEvents:"auto"},[$]:{position:"absolute",zIndex:n,maxWidth:"100vw",transition:`all ${r}`,"&-hidden":{display:"none"}},[`&-left > ${$}`]:{top:0,bottom:0,left:{_skip_check_:!0,value:0},boxShadow:e.boxShadowDrawerLeft},[`&-right > ${$}`]:{top:0,right:{_skip_check_:!0,value:0},bottom:0,boxShadow:e.boxShadowDrawerRight},[`&-top > ${$}`]:{top:0,insetInline:0,boxShadow:e.boxShadowDrawerUp},[`&-bottom > ${$}`]:{bottom:0,insetInline:0,boxShadow:e.boxShadowDrawerDown},[`${a}-content`]:{display:"flex",flexDirection:"column",width:"100%",height:"100%",overflow:"auto",background:o,pointerEvents:"auto"},[`${a}-header`]:{display:"flex",flex:0,alignItems:"center",padding:`${(0,R.unit)(d)} ${(0,R.unit)(c)}`,fontSize:u,lineHeight:f,borderBottom:`${(0,R.unit)(p)} ${m} ${h}`,"&-title":{display:"flex",flex:1,alignItems:"center",minWidth:0,minHeight:0}},[`${a}-extra`]:{flex:"none"},[`${a}-close`]:Object.assign({display:"inline-flex",width:C(u).add(i).equal(),height:C(u).add(i).equal(),borderRadius:t,justifyContent:"center",alignItems:"center",color:g,fontWeight:j,fontSize:u,fontStyle:"normal",lineHeight:1,textAlign:"center",textTransform:"none",textDecoration:"none",background:"transparent",border:0,cursor:"pointer",transition:`all ${s}`,textRendering:"auto",[`&${a}-close-end`]:{marginInlineStart:x},[`&:not(${a}-close-end)`]:{marginInlineEnd:x},"&:hover":{color:y,backgroundColor:b,textDecoration:"none"},"&:active":{backgroundColor:v}},(0,D.genFocusStyle)(e)),[`${a}-title`]:{flex:1,margin:0,fontWeight:e.fontWeightStrong,fontSize:u,lineHeight:f},[`${a}-body`]:{flex:1,minWidth:0,minHeight:0,padding:c,overflow:"auto",[`${a}-body-skeleton`]:{width:"100%",height:"100%",display:"flex",justifyContent:"center"}},[`${a}-footer`]:{flexShrink:0,padding:`${(0,R.unit)(k)} ${(0,R.unit)(S)}`,borderTop:`${(0,R.unit)(p)} ${m} ${h}`},"&-rtl":{direction:"rtl"}}}})(t),(e=>{let{componentCls:t,motionDurationSlow:a}=e;return{[t]:{[`${t}-mask-motion`]:F(0,a),[`${t}-panel-motion`]:["left","right","top","bottom"].reduce((e,t)=>{let n;return Object.assign(Object.assign({},e),{[`&-${t}`]:[F(.7,a),B({transform:(n="100%",({left:`translateX(-${n})`,right:`translateX(${n})`,top:`translateY(-${n})`,bottom:`translateY(${n})`})[t])},{transform:"none"})]})},{})}}})(t)]},e=>({zIndexPopup:e.zIndexPopupBase,footerPaddingBlock:e.paddingXS,footerPaddingInline:e.padding}));var L=function(e,t){var a={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(a[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(a[n[l]]=e[n[l]]);return a};let W={distance:180},A=e=>{let{rootClassName:n,width:l,height:o,size:r="default",mask:s=!0,push:i=W,open:d,afterOpenChange:c,onClose:u,prefixCls:f,getContainer:p,panelRef:m=null,style:x,className:g,"aria-labelledby":y,visible:b,afterVisibleChange:v,maskStyle:w,drawerStyle:_,contentWrapperStyle:N,destroyOnClose:R,destroyOnHidden:D}=e,T=L(e,["rootClassName","width","height","size","mask","push","open","afterOpenChange","onClose","prefixCls","getContainer","panelRef","style","className","aria-labelledby","visible","afterVisibleChange","maskStyle","drawerStyle","contentWrapperStyle","destroyOnClose","destroyOnHidden"]),M=(0,k.default)(),B=T.title?M:void 0,{getPopupContainer:F,getPrefixCls:A,direction:K,className:H,style:U,classNames:q,styles:X}=(0,E.useComponentConfig)("drawer"),Y=A("drawer",f),[J,G,V]=P(Y),Z=void 0===p&&F?()=>F(document.body):p,Q=(0,a.default)({"no-mask":!s,[`${Y}-rtl`]:"rtl"===K},n,G,V),ee=t.useMemo(()=>null!=l?l:"large"===r?736:378,[l,r]),et=t.useMemo(()=>null!=o?o:"large"===r?736:378,[o,r]),ea={motionName:(0,$.getTransitionName)(Y,"mask-motion"),motionAppear:!0,motionEnter:!0,motionLeave:!0,motionDeadline:500},en=(0,z.usePanelRef)(),el=(0,h.composeRef)(m,en),[eo,er]=(0,C.useZIndex)("Drawer",T.zIndex),{classNames:es={},styles:ei={}}=T;return J(t.createElement(S.default,{form:!0,space:!0},t.createElement(O.default.Provider,{value:er},t.createElement(j,Object.assign({prefixCls:Y,onClose:u,maskMotion:ea,motion:e=>({motionName:(0,$.getTransitionName)(Y,`panel-motion-${e}`),motionAppear:!0,motionEnter:!0,motionLeave:!0,motionDeadline:500})},T,{classNames:{mask:(0,a.default)(es.mask,q.mask),content:(0,a.default)(es.content,q.content),wrapper:(0,a.default)(es.wrapper,q.wrapper)},styles:{mask:Object.assign(Object.assign(Object.assign({},ei.mask),w),X.mask),content:Object.assign(Object.assign(Object.assign({},ei.content),_),X.content),wrapper:Object.assign(Object.assign(Object.assign({},ei.wrapper),N),X.wrapper)},open:null!=d?d:b,mask:s,push:i,width:ee,height:et,style:Object.assign(Object.assign({},U),x),className:(0,a.default)(H,g),rootClassName:Q,getContainer:Z,afterOpenChange:null!=c?c:v,panelRef:el,zIndex:eo,"aria-labelledby":null!=y?y:B,destroyOnClose:null!=D?D:R}),t.createElement(I,Object.assign({prefixCls:Y},T,{ariaId:B,onClose:u}))))))};A._InternalPanelDoNotUseOrYouWillBeFired=e=>{let{prefixCls:n,style:l,className:o,placement:r="right"}=e,s=L(e,["prefixCls","style","className","placement"]),{getPrefixCls:i}=t.useContext(E.ConfigContext),d=i("drawer",n),[c,u,f]=P(d),p=(0,a.default)(d,`${d}-pure`,`${d}-${r}`,u,f,o);return c(t.createElement("div",{className:p,style:l},t.createElement(I,Object.assign({prefixCls:d},s))))},e.s(["Drawer",0,A],608856)},425656,e=>{"use strict";var t=e.i(843476),a=e.i(271645),n=e.i(464571),l=e.i(362024),o=e.i(608856),r=e.i(21548),s=e.i(482725),i=e.i(291542),d=e.i(592968),c=e.i(898586),u=e.i(91979),f=e.i(602869);let{Text:p}=c.Typography,m={pending:"#a1a1aa",running:"#3b82f6",paused:"#f59e0b",completed:"#22c55e",failed:"#ef4444"},h={"step.started":{bar:"#f0fdf4",border:"#86efac",text:"#16a34a"},"step.failed":{bar:"#fef2f2",border:"#fca5a5",text:"#dc2626"},"hook.waiting":{bar:"#fffbeb",border:"#fcd34d",text:"#d97706"},"hook.received":{bar:"#eff6ff",border:"#93c5fd",text:"#2563eb"}};function x(e){let t=Date.now()-new Date(e).getTime();if(isNaN(t))return e;let a=Math.floor(t/1e3);if(a<60)return`${a}s ago`;let n=Math.floor(a/60);if(n<60)return`${n}m ago`;let l=Math.floor(n/60);return l<24?`${l}h ago`:`${Math.floor(l/24)}d ago`}function g(e){return e<0?"":e<1e3?`${e}ms`:`${(e/1e3).toFixed(1)}s`}function y(e){let t=e.metadata?.title;return t?String(t):e.workflow_type??e.run_id.slice(0,8)}function b(e){return e.slice(0,8)}let v=({status:e,size:a=8})=>(0,t.jsx)("span",{style:{display:"inline-block",width:a,height:a,borderRadius:"50%",background:m[e]??"#a1a1aa",flexShrink:0}}),w=({value:e})=>{let[n,l]=(0,a.useState)(!1);return e.length<=120?(0,t.jsx)("span",{style:{color:"#27272a",wordBreak:"break-all"},children:e}):(0,t.jsxs)("span",{style:{color:"#27272a",wordBreak:"break-all"},children:[n?e:e.slice(0,120)+"…",(0,t.jsx)("button",{onClick:()=>l(e=>!e),style:{background:"none",border:"none",padding:"0 4px",cursor:"pointer",color:"#2563eb",fontSize:11,flexShrink:0},children:n?"less":"more"})]})},j=({run:e})=>{let a=e.metadata??{},n=[{key:"state",label:"state"},{key:"worktree_path",label:"worktree"},{key:"grill_session_id",label:"grill session"},{key:"session_id",label:"session"}],l=new Set(["title",...n.map(e=>e.key)]),o=Object.entries(a).filter(([e,t])=>!l.has(e)&&null!=t&&""!==t);return(0,t.jsxs)("div",{style:{borderRadius:8,border:"1px solid #e4e4e7",marginBottom:16,overflow:"hidden"},children:[(0,t.jsxs)("div",{style:{padding:"14px 20px",borderBottom:"1px solid #f4f4f5",display:"flex",alignItems:"center",gap:10},children:[(0,t.jsx)(v,{status:e.status,size:10}),(0,t.jsx)("span",{style:{fontSize:14,fontWeight:600,color:"#18181b",flex:1},children:y(e)}),(0,t.jsx)("span",{style:{fontFamily:"monospace",fontSize:11,color:"#a1a1aa",background:"#f4f4f5",padding:"2px 8px",borderRadius:4},children:b(e.run_id)}),(0,t.jsx)("span",{style:{fontSize:11,color:"#a1a1aa",background:"#f4f4f5",padding:"2px 8px",borderRadius:4},children:e.workflow_type})]}),(0,t.jsxs)("div",{style:{padding:"12px 20px",display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:"8px 24px",fontFamily:"monospace",fontSize:12},children:[(0,t.jsx)(k,{label:"status",children:(0,t.jsx)("span",{style:{textTransform:"capitalize",color:"#27272a"},children:e.status})}),(0,t.jsx)(k,{label:"created",children:(0,t.jsx)("span",{style:{color:"#27272a"},children:x(e.created_at)})}),a.pr_url&&(0,t.jsx)(k,{label:"pr",children:(0,t.jsx)("a",{href:String(a.pr_url),target:"_blank",rel:"noopener noreferrer",style:{color:"#2563eb",textDecoration:"none",wordBreak:"break-all"},children:String(a.pr_url)})}),n.map(({key:e,label:n})=>{let l=a[e];if(null==l||""===l)return null;let o="object"==typeof l?JSON.stringify(l):String(l);return(0,t.jsx)(k,{label:n,children:(0,t.jsx)(w,{value:o})},e)}),o.map(([e,a])=>{let n="object"==typeof a?JSON.stringify(a):String(a);return(0,t.jsx)(k,{label:e,children:(0,t.jsx)(w,{value:n})},e)})]})]})},k=({label:e,children:a})=>(0,t.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:1},children:[(0,t.jsx)("span",{style:{fontSize:10,color:"#a1a1aa",textTransform:"uppercase",letterSpacing:"0.06em"},children:e}),(0,t.jsx)("span",{style:{fontSize:12},children:a})]}),S=({run:e,events:n})=>{if(0===n.length)return(0,t.jsx)("div",{style:{padding:"16px 0",color:"#a1a1aa",fontSize:12,fontFamily:"monospace"},children:"No events recorded"});let l=new Date(e.created_at).getTime(),o=Math.max(...n.map(e=>new Date(e.created_at).getTime())),r=Math.max(o-l,1),s=g(o-l);return(0,t.jsxs)("div",{style:{fontFamily:"monospace",fontSize:12},children:[(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"160px 1fr",gap:"0 12px",marginBottom:2},children:[(0,t.jsx)("div",{}),(0,t.jsx)("div",{style:{position:"relative",height:16},children:[0,100].map(e=>(0,t.jsx)("span",{style:{position:"absolute",left:`${e}%`,transform:100===e?"translateX(-100%)":void 0,fontSize:10,color:"#a1a1aa"},children:0===e?"0":s},e))})]}),(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"160px 1fr",gap:"0 12px",marginBottom:4},children:[(0,t.jsx)("div",{style:{color:"#3f3f46",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",paddingTop:2},children:y(e)}),(0,t.jsx)("div",{style:{height:24,background:"#f4f4f5",border:"1px solid #d4d4d8",borderRadius:4,display:"flex",alignItems:"center",paddingLeft:8},children:(0,t.jsx)("span",{style:{color:"#71717a",fontSize:11},children:s})})]}),(0,t.jsx)("div",{style:{display:"grid",gridTemplateColumns:"160px 1fr",gap:"0 12px",rowGap:3},children:n.map(e=>{let s=new Date(e.created_at).getTime(),i=(s-l)/r*100,c=n.findIndex(t=>t.sequence_number>e.sequence_number),u=c>=0?new Date(n[c].created_at).getTime():o+Math.max(.12*r,500),f=Math.max(8,(u-s)/r*100),p=h[e.event_type]??{bar:"#f4f4f5",border:"#d4d4d8",text:"#52525b"},m=g(u-s);return(0,t.jsxs)(a.default.Fragment,{children:[(0,t.jsx)("div",{style:{color:p.text,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",paddingTop:2,paddingLeft:12},children:e.step_name||e.event_type}),(0,t.jsx)("div",{style:{position:"relative",height:24},children:(0,t.jsx)(d.Tooltip,{title:(0,t.jsxs)("div",{style:{fontFamily:"monospace",fontSize:11,lineHeight:1.6},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{color:"#a1a1aa"},children:"type: "}),(0,t.jsx)("span",{style:{color:p.text},children:e.event_type})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{color:"#a1a1aa"},children:"step: "}),e.step_name]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{color:"#a1a1aa"},children:"seq: "}),e.sequence_number]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{color:"#a1a1aa"},children:"time: "}),x(e.created_at)]}),e.data&&Object.keys(e.data).length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{color:"#a1a1aa"},children:"data: "}),JSON.stringify(e.data)]})]}),children:(0,t.jsxs)("div",{style:{position:"absolute",left:`${Math.min(i,92)}%`,width:`${Math.min(f,100-Math.min(i,92))}%`,height:"100%",background:p.bar,border:`1px solid ${p.border}`,borderRadius:4,display:"flex",alignItems:"center",paddingLeft:8,cursor:"default",overflow:"hidden",gap:6},children:[(0,t.jsx)("span",{style:{color:p.text,whiteSpace:"nowrap",fontSize:11},children:e.event_type}),m&&(0,t.jsx)("span",{style:{color:"#a1a1aa",whiteSpace:"nowrap",fontSize:11},children:m})]})})})]},e.event_id)})})]})},C=({msg:e})=>{let a={user:"#2563eb",assistant:"#16a34a",system:"#7c3aed",tool_result:"#d97706"}[e.role]??"#52525b";return(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"80px 1fr",gap:"0 16px",padding:"10px 0",borderBottom:"1px solid #f4f4f5",fontFamily:"monospace",fontSize:12,alignItems:"start"},children:[(0,t.jsxs)("span",{style:{color:a,paddingTop:1},children:["[",e.role,"]"]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{color:"#27272a",lineHeight:1.6,whiteSpace:"pre-wrap",wordBreak:"break-word",display:"block"},children:e.content}),(0,t.jsx)("span",{style:{color:"#a1a1aa",fontSize:11,marginTop:2,display:"block"},children:x(e.created_at)})]})]})},$=({accessToken:e})=>{let[d,c]=(0,a.useState)([]),[p,m]=(0,a.useState)(!1),[h,g]=(0,a.useState)(null),[w,k]=(0,a.useState)([]),[$,O]=(0,a.useState)([]),[E,z]=(0,a.useState)(!1),[_,N]=(0,a.useState)(!1),I=(0,a.useCallback)(async()=>{if(e){m(!0);try{let t=await fetch(`${f.proxyBaseUrl??""}/v1/workflows/runs?limit=100`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw Error(`HTTP ${t.status}`);let a=await t.json();c(a.runs??[])}catch(e){console.error("workflow runs fetch failed:",e)}finally{m(!1)}}},[e]),R=(0,a.useCallback)(async t=>{if(e){g(t),N(!0),z(!0),k([]),O([]);try{let a=f.proxyBaseUrl??"",[n,l]=await Promise.all([fetch(`${a}/v1/workflows/runs/${t.run_id}/events`,{headers:{Authorization:`Bearer ${e}`}}),fetch(`${a}/v1/workflows/runs/${t.run_id}/messages`,{headers:{Authorization:`Bearer ${e}`}})]),o=n.ok?await n.json():{events:[]},r=l.ok?await l.json():{messages:[]};k([...o.events??[]].sort((e,t)=>e.sequence_number-t.sequence_number)),O([...r.messages??[]].sort((e,t)=>e.sequence_number-t.sequence_number))}catch(e){console.error("workflow run detail fetch failed:",e)}finally{z(!1)}}},[e]);(0,a.useEffect)(()=>{I()},[I]);let D=[{title:"Run",dataIndex:"run_id",key:"run",render:(e,a)=>(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,t.jsx)(v,{status:a.status,size:7}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{style:{fontSize:13,color:"#18181b",fontWeight:500,lineHeight:1.4},children:y(a)}),(0,t.jsx)("div",{style:{fontFamily:"monospace",fontSize:11,color:"#a1a1aa"},children:b(a.run_id)})]})]})},{title:"Type",dataIndex:"workflow_type",key:"workflow_type",render:e=>(0,t.jsx)("span",{style:{fontFamily:"monospace",fontSize:12,color:"#71717a"},children:e})},{title:"Status",dataIndex:"status",key:"status",render:(e,a)=>{let n=a.metadata?.state;return(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:6},children:[(0,t.jsx)(v,{status:e,size:7}),(0,t.jsx)("span",{style:{fontSize:12,color:"#52525b",textTransform:"capitalize"},children:n??e})]})}},{title:"Created",dataIndex:"created_at",key:"created_at",render:e=>(0,t.jsx)("span",{style:{fontSize:12,color:"#a1a1aa"},children:x(e)})}];return(0,t.jsxs)("div",{style:{width:"100%",padding:"24px 32px",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',minHeight:"calc(100vh - 64px)",background:"#fff"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:20},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{style:{fontSize:18,fontWeight:600,color:"#18181b"},children:"Workflow Runs"}),(0,t.jsx)("div",{style:{fontSize:13,color:"#71717a",marginTop:2},children:"Durable state tracking for agents and automated workflows"})]}),(0,t.jsx)(n.Button,{icon:(0,t.jsx)(u.ReloadOutlined,{}),onClick:I,loading:p,style:{color:"#71717a",borderColor:"#e4e4e7"},children:"Refresh"})]}),(0,t.jsx)("div",{className:"rounded-lg custom-border overflow-x-auto w-full",children:(0,t.jsx)(i.Table,{dataSource:d,columns:D,rowKey:"run_id",loading:p,size:"small",pagination:{pageSize:50,hideOnSinglePage:!0,size:"small"},onRow:e=>({onClick:()=>R(e),style:{cursor:"pointer"}}),locale:{emptyText:(0,t.jsx)(r.Empty,{description:(0,t.jsx)("span",{style:{color:"#a1a1aa",fontSize:13},children:"No workflow runs yet"}),image:r.Empty.PRESENTED_IMAGE_SIMPLE})},className:"[&_.ant-table-cell]:py-0.5 [&_.ant-table-thead_.ant-table-cell]:py-1",style:{border:"none"}})}),(0,t.jsx)(o.Drawer,{open:_,onClose:()=>N(!1),width:680,title:null,closable:!1,bodyStyle:{padding:0},styles:{body:{padding:0}},children:h?E?(0,t.jsx)("div",{style:{display:"flex",justifyContent:"center",padding:80},children:(0,t.jsx)(s.Spin,{})}):(0,t.jsxs)("div",{style:{padding:"24px 28px",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:16},children:[(0,t.jsx)("button",{onClick:()=>N(!1),style:{background:"none",border:"none",cursor:"pointer",padding:"4px 0",fontSize:12,color:"#a1a1aa",display:"flex",alignItems:"center",gap:4},children:"← close"}),(0,t.jsx)(n.Button,{size:"small",icon:(0,t.jsx)(u.ReloadOutlined,{}),onClick:()=>R(h),loading:E,style:{color:"#71717a",borderColor:"#e4e4e7"},children:"Refresh"})]}),(0,t.jsx)(j,{run:h}),(0,t.jsx)(l.Collapse,{defaultActiveKey:["timeline"],ghost:!1,style:{border:"1px solid #e4e4e7",borderRadius:8,overflow:"hidden"},items:[{key:"timeline",label:(0,t.jsxs)("span",{style:{fontSize:12,fontWeight:500,color:"#3f3f46"},children:["Timeline",(0,t.jsxs)("span",{style:{marginLeft:6,fontSize:11,color:"#a1a1aa",fontWeight:400},children:[w.length," ",1===w.length?"event":"events"]})]}),children:(0,t.jsx)("div",{style:{padding:"4px 4px 12px"},children:(0,t.jsx)(S,{run:h,events:w})})},{key:"messages",label:(0,t.jsxs)("span",{style:{fontSize:12,fontWeight:500,color:"#3f3f46"},children:["Messages",(0,t.jsx)("span",{style:{marginLeft:6,fontSize:11,color:"#a1a1aa",fontWeight:400},children:$.length})]}),children:0===$.length?(0,t.jsx)("div",{style:{padding:"12px 4px",color:"#a1a1aa",fontSize:12,fontFamily:"monospace"},children:"No messages"}):(0,t.jsx)("div",{style:{paddingBottom:4},children:$.map(e=>(0,t.jsx)(C,{msg:e},e.message_id))})}]})]}):null})]})};var O=e.i(135214);e.s(["default",0,function(){let{accessToken:e}=(0,O.default)();return(0,t.jsx)($,{accessToken:e})}],425656)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02dxw4eubg_rq.js b/litellm/proxy/_experimental/out/_next/static/chunks/02dxw4eubg_rq.js new file mode 100644 index 00000000000..0b24a6199e3 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02dxw4eubg_rq.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let n=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],i=e=>({_s:e,status:n[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),a=e=>e?6:5,l=(e,t,r,o,n)=>{clearTimeout(o.current);let a=i(e);t(a),r.current=a,n&&n({current:a})};var s=e.i(480731),c=e.i(444755),d=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var g=e.i(95779);let m={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,d.getColorClassNames)(t,g.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,d.getColorClassNames)(t,g.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,d.getColorClassNames)(t,g.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,d.getColorClassNames)(t,g.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,d.getColorClassNames)(t,g.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,g.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,c.tremorTwMerge)((0,d.getColorClassNames)(t,g.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,d.getColorClassNames)(t,g.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,d.getColorClassNames)(t,g.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,g.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,d.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:n,needMargin:i,transitionStatus:a})=>{let l=i?r===s.HorizontalPositions.Left?(0,c.tremorTwMerge)("-ml-1","mr-1.5"):(0,c.tremorTwMerge)("-mr-1","ml-1.5"):"",d=(0,c.tremorTwMerge)("w-0 h-0"),g={default:d,entering:d,entered:t,exiting:t,exited:d};return e?o.default.createElement(u,{className:(0,c.tremorTwMerge)(f("icon"),"animate-spin shrink-0",l,g.default,g[a]),style:{transition:"width 150ms"}}):o.default.createElement(n,{className:(0,c.tremorTwMerge)(f("icon"),"shrink-0",t,l)})},b=o.default.forwardRef((e,n)=>{let{icon:u,iconPosition:g=s.HorizontalPositions.Left,size:b=s.Sizes.SM,color:C,variant:x="primary",disabled:k,loading:v=!1,loadingText:y,children:$,tooltip:w,className:S}=e,E=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),N=v||k,B=void 0!==u||v,z=v&&y,O=!(!$&&!z),T=(0,c.tremorTwMerge)(m[b].height,m[b].width),P="light"!==x?(0,c.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",j=p(x,C),M=("light"!==x?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:I,getReferenceProps:R}=(0,r.useTooltip)(300),[A,W]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:n,timeout:s,initialEntered:c,mountOnEnter:d,unmountOnExit:u,onStateChange:g}={})=>{let[m,p]=(0,o.useState)(()=>i(c?2:a(d))),f=(0,o.useRef)(m),h=(0,o.useRef)(0),[b,C]="object"==typeof s?[s.enter,s.exit]:[s,s],x=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return a(t)}})(f.current._s,u);e&&l(e,p,f,h,g)},[g,u]);return[m,(0,o.useCallback)(o=>{let i=e=>{switch(l(e,p,f,h,g),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(x,b));break;case 4:C>=0&&(h.current=((...e)=>setTimeout(...e))(x,C));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||i(e+1)},0)}},s=f.current.isEnter;"boolean"!=typeof o&&(o=!s),o?s||i(e?+!r:2):s&&i(t?n?3:4:a(u))},[x,g,e,t,r,n,b,C,u]),x]})({timeout:50});return(0,o.useEffect)(()=>{W(v)},[v]),o.default.createElement("button",Object.assign({ref:(0,d.mergeRefs)([n,I.refs.setReference]),className:(0,c.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",P,M.paddingX,M.paddingY,M.fontSize,j.textColor,j.bgColor,j.borderColor,j.hoverBorderColor,N?"opacity-50 cursor-not-allowed":(0,c.tremorTwMerge)(p(x,C).hoverTextColor,p(x,C).hoverBgColor,p(x,C).hoverBorderColor),S),disabled:N},R,E),o.default.createElement(r.default,Object.assign({text:w},I)),B&&g!==s.HorizontalPositions.Right?o.default.createElement(h,{loading:v,iconSize:T,iconPosition:g,Icon:u,transitionStatus:A.status,needMargin:O}):null,z||$?o.default.createElement("span",{className:(0,c.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},z?y:$):null,B&&g===s.HorizontalPositions.Right?o.default.createElement(h,{loading:v,iconSize:T,iconPosition:g,Icon:u,transitionStatus:A.status,needMargin:O}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),o=e.i(673706),n=e.i(271645);let i=n.default.forwardRef((e,i)=>{let{color:a,className:l,children:s}=e;return n.default.createElement("p",{ref:i,className:(0,r.tremorTwMerge)("text-tremor-default",a?(0,o.getColorClassNames)(a,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),l)},s)});i.displayName="Text",e.s(["default",0,i],936325),e.s(["Text",0,i],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),n=e.i(95779),i=e.i(444755),a=e.i(673706);let l=(0,a.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:c="",decorationColor:d,children:u,className:g}=e,m=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,i.tremorTwMerge)(l("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",d?(0,a.getColorClassNames)(d,n.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(c),g)},m),u)});s.displayName="Card",e.s(["Card",0,s],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),n=e.i(673706),i=e.i(271645);let a=i.default.forwardRef((e,a)=>{let{color:l,children:s,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return i.default.createElement("p",Object.assign({ref:a,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",l?(0,n.getColorClassNames)(l,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",c)},d),s)});a.displayName="Title",e.s(["Title",0,a],629569)},312361,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),o=e.i(242064),n=e.i(517455);e.i(296059);var i=e.i(915654),a=e.i(183293),l=e.i(246422),s=e.i(838378);let c=(0,l.genStyleHooks)("Divider",e=>{let t=(0,s.mergeToken)(e,{dividerHorizontalWithTextGutterMargin:e.margin,sizePaddingEdgeHorizontal:0});return[(e=>{let{componentCls:t,sizePaddingEdgeHorizontal:r,colorSplit:o,lineWidth:n,textPaddingInline:l,orientationMargin:s,verticalMarginInline:c}=e;return{[t]:Object.assign(Object.assign({},(0,a.resetComponent)(e)),{borderBlockStart:`${(0,i.unit)(n)} solid ${o}`,"&-vertical":{position:"relative",top:"-0.06em",display:"inline-block",height:"0.9em",marginInline:c,marginBlock:0,verticalAlign:"middle",borderTop:0,borderInlineStart:`${(0,i.unit)(n)} solid ${o}`},"&-horizontal":{display:"flex",clear:"both",width:"100%",minWidth:"100%",margin:`${(0,i.unit)(e.marginLG)} 0`},[`&-horizontal${t}-with-text`]:{display:"flex",alignItems:"center",margin:`${(0,i.unit)(e.dividerHorizontalWithTextGutterMargin)} 0`,color:e.colorTextHeading,fontWeight:500,fontSize:e.fontSizeLG,whiteSpace:"nowrap",textAlign:"center",borderBlockStart:`0 ${o}`,"&::before, &::after":{position:"relative",width:"50%",borderBlockStart:`${(0,i.unit)(n)} solid transparent`,borderBlockStartColor:"inherit",borderBlockEnd:0,transform:"translateY(50%)",content:"''"}},[`&-horizontal${t}-with-text-start`]:{"&::before":{width:`calc(${s} * 100%)`},"&::after":{width:`calc(100% - ${s} * 100%)`}},[`&-horizontal${t}-with-text-end`]:{"&::before":{width:`calc(100% - ${s} * 100%)`},"&::after":{width:`calc(${s} * 100%)`}},[`${t}-inner-text`]:{display:"inline-block",paddingBlock:0,paddingInline:l},"&-dashed":{background:"none",borderColor:o,borderStyle:"dashed",borderWidth:`${(0,i.unit)(n)} 0 0`},[`&-horizontal${t}-with-text${t}-dashed`]:{"&::before, &::after":{borderStyle:"dashed none none"}},[`&-vertical${t}-dashed`]:{borderInlineStartWidth:n,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},"&-dotted":{background:"none",borderColor:o,borderStyle:"dotted",borderWidth:`${(0,i.unit)(n)} 0 0`},[`&-horizontal${t}-with-text${t}-dotted`]:{"&::before, &::after":{borderStyle:"dotted none none"}},[`&-vertical${t}-dotted`]:{borderInlineStartWidth:n,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},[`&-plain${t}-with-text`]:{color:e.colorText,fontWeight:"normal",fontSize:e.fontSize},[`&-horizontal${t}-with-text-start${t}-no-default-orientation-margin-start`]:{"&::before":{width:0},"&::after":{width:"100%"},[`${t}-inner-text`]:{paddingInlineStart:r}},[`&-horizontal${t}-with-text-end${t}-no-default-orientation-margin-end`]:{"&::before":{width:"100%"},"&::after":{width:0},[`${t}-inner-text`]:{paddingInlineEnd:r}}})}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-horizontal":{[`&${t}`]:{"&-sm":{marginBlock:e.marginXS},"&-md":{marginBlock:e.margin}}}}}})(t)]},e=>({textPaddingInline:"1em",orientationMargin:.05,verticalMarginInline:e.marginXS}),{unitless:{orientationMargin:!0}});var d=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,o=Object.getOwnPropertySymbols(e);nt.indexOf(o[n])&&Object.prototype.propertyIsEnumerable.call(e,o[n])&&(r[o[n]]=e[o[n]]);return r};let u={small:"sm",middle:"md"};e.s(["Divider",0,e=>{let{getPrefixCls:i,direction:a,className:l,style:s}=(0,o.useComponentConfig)("divider"),{prefixCls:g,type:m="horizontal",orientation:p="center",orientationMargin:f,className:h,rootClassName:b,children:C,dashed:x,variant:k="solid",plain:v,style:y,size:$}=e,w=d(e,["prefixCls","type","orientation","orientationMargin","className","rootClassName","children","dashed","variant","plain","style","size"]),S=i("divider",g),[E,N,B]=c(S),z=u[(0,n.default)($)],O=!!C,T=t.useMemo(()=>"left"===p?"rtl"===a?"end":"start":"right"===p?"rtl"===a?"start":"end":p,[a,p]),P="start"===T&&null!=f,j="end"===T&&null!=f,M=(0,r.default)(S,l,N,B,`${S}-${m}`,{[`${S}-with-text`]:O,[`${S}-with-text-${T}`]:O,[`${S}-dashed`]:!!x,[`${S}-${k}`]:"solid"!==k,[`${S}-plain`]:!!v,[`${S}-rtl`]:"rtl"===a,[`${S}-no-default-orientation-margin-start`]:P,[`${S}-no-default-orientation-margin-end`]:j,[`${S}-${z}`]:!!z},h,b),I=t.useMemo(()=>"number"==typeof f?f:/^\d+$/.test(f)?Number(f):f,[f]);return E(t.createElement("div",Object.assign({className:M,style:Object.assign(Object.assign({},s),y)},w,{role:"separator"}),C&&"vertical"!==m&&t.createElement("span",{className:`${S}-inner-text`,style:{marginInlineStart:P?I:void 0,marginInlineEnd:j?I:void 0}},C)))}],312361)},56456,e=>{"use strict";var t=e.i(739295);e.s(["LoadingOutlined",()=>t.default])},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),o=e.i(201072),n=e.i(121229),i=e.i(726289),a=e.i(864517),l=e.i(343794),s=e.i(529681),c=e.i(242064),d=e.i(931067),u=e.i(209428),g=e.i(703923),m={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},p=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),o=!1;e.current.forEach(function(e){if(e){o=!0;var n=e.style;n.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(n.transitionDuration="0s, 0s")}}),o&&(r.current=Date.now())}),e.current},f=e.i(410160),h=e.i(392221),b=e.i(654310),C=0,x=(0,b.default)();let k=function(e){var r=t.useState(),o=(0,h.default)(r,2),n=o[0],i=o[1];return t.useEffect(function(){var e;i("rc_progress_".concat((x?(e=C,C+=1):e="TEST_OR_SSR",e)))},[]),e||n};var v=function(e){var r=e.bg,o=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},o)};function y(e,t){return Object.keys(e).map(function(r){var o=parseFloat(r),n="".concat(Math.floor(o*t),"%");return"".concat(e[r]," ").concat(n)})}var $=t.forwardRef(function(e,r){var o=e.prefixCls,n=e.color,i=e.gradientId,a=e.radius,l=e.style,s=e.ptg,c=e.strokeLinecap,d=e.strokeWidth,u=e.size,g=e.gapDegree,m=n&&"object"===(0,f.default)(n),p=u/2,h=t.createElement("circle",{className:"".concat(o,"-circle-path"),r:a,cx:p,cy:p,stroke:m?"#FFF":void 0,strokeLinecap:c,strokeWidth:d,opacity:+(0!==s),style:l,ref:r});if(!m)return h;var b="".concat(i,"-conic"),C=y(n,(360-g)/360),x=y(n,1),k="conic-gradient(from ".concat(g?"".concat(180+g/2,"deg"):"0deg",", ").concat(C.join(", "),")"),$="linear-gradient(to ".concat(g?"bottom":"top",", ").concat(x.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:u,height:u,mask:"url(#".concat(b,")")},t.createElement(v,{bg:$},t.createElement(v,{bg:k}))))}),w=function(e,t,r,o,n,i,a,l,s,c){var d=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,u=(100-o)/100*t;return"round"===s&&100!==o&&(u+=c/2)>=t&&(u=t-.01),{stroke:"string"==typeof l?l:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:u+d,transform:"rotate(".concat(n+r/100*360*((360-i)/360)+(0===i?0:({bottom:0,top:180,left:90,right:-90})[a]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let N=function(e){var r,o,n,i,a=(0,u.default)((0,u.default)({},m),e),s=a.id,c=a.prefixCls,h=a.steps,b=a.strokeWidth,C=a.trailWidth,x=a.gapDegree,v=void 0===x?0:x,y=a.gapPosition,N=a.trailColor,B=a.strokeLinecap,z=a.style,O=a.className,T=a.strokeColor,P=a.percent,j=(0,g.default)(a,S),M=k(s),I="".concat(M,"-gradient"),R=50-b/2,A=2*Math.PI*R,W=v>0?90+v/2:-90,X=(360-v)/360*A,D="object"===(0,f.default)(h)?h:{count:h,gap:2},L=D.count,H=D.gap,_=E(P),F=E(T),Y=F.find(function(e){return e&&"object"===(0,f.default)(e)}),G=Y&&"object"===(0,f.default)(Y)?"butt":B,K=w(A,X,0,100,W,v,y,N,G,b),V=p();return t.createElement("svg",(0,d.default)({className:(0,l.default)("".concat(c,"-circle"),O),viewBox:"0 0 ".concat(100," ").concat(100),style:z,id:s,role:"presentation"},j),!L&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:R,cx:50,cy:50,stroke:N,strokeLinecap:G,strokeWidth:C||b,style:K}),L?(r=Math.round(L*(_[0]/100)),o=100/L,n=0,Array(L).fill(null).map(function(e,i){var a=i<=r-1?F[0]:N,l=a&&"object"===(0,f.default)(a)?"url(#".concat(I,")"):void 0,s=w(A,X,n,o,W,v,y,a,"butt",b,H);return n+=(X-s.strokeDashoffset+H)*100/X,t.createElement("circle",{key:i,className:"".concat(c,"-circle-path"),r:R,cx:50,cy:50,stroke:l,strokeWidth:b,opacity:1,style:s,ref:function(e){V[i]=e}})})):(i=0,_.map(function(e,r){var o=F[r]||F[F.length-1],n=w(A,X,i,e,W,v,y,o,G,b);return i+=e,t.createElement($,{key:r,color:o,ptg:e,radius:R,prefixCls:c,gradientId:I,style:n,strokeLinecap:G,strokeWidth:b,gapDegree:v,ref:function(e){V[r]=e},size:100})}).reverse()))};var B=e.i(491816);e.i(765846);var z=e.i(896091);function O(e){return!e||e<0?0:e>100?100:e}function T({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let P=(e,t,r)=>{var o,n,i,a;let l=-1,s=-1;if("step"===t){let t=r.steps,o=r.strokeWidth;"string"==typeof e||void 0===e?(l="small"===e?2:14,s=null!=o?o:8):"number"==typeof e?[l,s]=[e,e]:[l=14,s=8]=Array.isArray(e)?e:[e.width,e.height],l*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?s=t||("small"===e?6:8):"number"==typeof e?[l,s]=[e,e]:[l=-1,s=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[l,s]="small"===e?[60,60]:[120,120]:"number"==typeof e?[l,s]=[e,e]:Array.isArray(e)&&(l=null!=(n=null!=(o=e[0])?o:e[1])?n:120,s=null!=(a=null!=(i=e[0])?i:e[1])?a:120));return[l,s]},j=e=>{let{prefixCls:r,trailColor:o=null,strokeLinecap:n="round",gapPosition:i,gapDegree:a,width:s=120,type:c,children:d,success:u,size:g=s,steps:m}=e,[p,f]=P(g,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/p*100,6));let b=t.useMemo(()=>a||0===a?a:"dashboard"===c?75:void 0,[a,c]),C=(({percent:e,success:t,successPercent:r})=>{let o=O(T({success:t,successPercent:r}));return[o,O(O(e)-o)]})(e),x="[object Object]"===Object.prototype.toString.call(e.strokeColor),k=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||z.presetPrimaryColors.green,t||null]})({success:u,strokeColor:e.strokeColor}),v=(0,l.default)(`${r}-inner`,{[`${r}-circle-gradient`]:x}),y=t.createElement(N,{steps:m,percent:m?C[1]:C,strokeWidth:h,trailWidth:h,strokeColor:m?k[1]:k,strokeLinecap:n,trailColor:o,prefixCls:r,gapDegree:b,gapPosition:i||"dashboard"===c&&"bottom"||void 0}),$=p<=20,w=t.createElement("div",{className:v,style:{width:p,height:f,fontSize:.15*p+6}},y,!$&&d);return $?t.createElement(B.default,{title:d},w):w};e.i(296059);var M=e.i(694758),I=e.i(915654),R=e.i(183293),A=e.i(246422),W=e.i(838378);let X="--progress-line-stroke-color",D="--progress-percent",L=e=>{let t=e?"100%":"-100%";return new M.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},H=(0,A.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,W.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,R.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${X})`]},height:"100%",width:`calc(1 / var(${D}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,I.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:L(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:L(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var _=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,o=Object.getOwnPropertySymbols(e);nt.indexOf(o[n])&&Object.prototype.propertyIsEnumerable.call(e,o[n])&&(r[o[n]]=e[o[n]]);return r};let F=e=>{let{prefixCls:r,direction:o,percent:n,size:i,strokeWidth:a,strokeColor:s,strokeLinecap:c="round",children:d,trailColor:u=null,percentPosition:g,success:m}=e,{align:p,type:f}=g,h=s&&"string"!=typeof s?((e,t)=>{let{from:r=z.presetPrimaryColors.blue,to:o=z.presetPrimaryColors.blue,direction:n="rtl"===t?"to left":"to right"}=e,i=_(e,["from","to","direction"]);if(0!==Object.keys(i).length){let e,t=(e=[],Object.keys(i).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:i[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${n}, ${t})`;return{background:r,[X]:r}}let a=`linear-gradient(${n}, ${r}, ${o})`;return{background:a,[X]:a}})(s,o):{[X]:s,background:s},b="square"===c||"butt"===c?0:void 0,[C,x]=P(null!=i?i:[-1,a||("small"===i?6:8)],"line",{strokeWidth:a}),k=Object.assign(Object.assign({width:`${O(n)}%`,height:x,borderRadius:b},h),{[D]:O(n)/100}),v=T(e),y={width:`${O(v)}%`,height:x,borderRadius:b,backgroundColor:null==m?void 0:m.strokeColor},$=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:u||void 0,borderRadius:b}},t.createElement("div",{className:(0,l.default)(`${r}-bg`,`${r}-bg-${f}`),style:k},"inner"===f&&d),void 0!==v&&t.createElement("div",{className:`${r}-success-bg`,style:y})),w="outer"===f&&"start"===p,S="outer"===f&&"end"===p;return"outer"===f&&"center"===p?t.createElement("div",{className:`${r}-layout-bottom`},$,d):t.createElement("div",{className:`${r}-outer`,style:{width:C<0?"100%":C}},w&&d,$,S&&d)},Y=e=>{let{size:r,steps:o,rounding:n=Math.round,percent:i=0,strokeWidth:a=8,strokeColor:s,trailColor:c=null,prefixCls:d,children:u}=e,g=n(i/100*o),[m,p]=P(null!=r?r:["small"===r?2:14,a],"step",{steps:o,strokeWidth:a}),f=m/o,h=Array.from({length:o});for(let e=0;et.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,o=Object.getOwnPropertySymbols(e);nt.indexOf(o[n])&&Object.prototype.propertyIsEnumerable.call(e,o[n])&&(r[o[n]]=e[o[n]]);return r};let K=["normal","exception","active","success"],V=t.forwardRef((e,d)=>{let u,{prefixCls:g,className:m,rootClassName:p,steps:f,strokeColor:h,percent:b=0,size:C="default",showInfo:x=!0,type:k="line",status:v,format:y,style:$,percentPosition:w={}}=e,S=G(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:N="outer"}=w,B=Array.isArray(h)?h[0]:h,z="string"==typeof h||Array.isArray(h)?h:void 0,M=t.useMemo(()=>{if(B){let e="string"==typeof B?B:Object.values(B)[0];return new r.FastColor(e).isLight()}return!1},[h]),I=t.useMemo(()=>{var t,r;let o=T(e);return Number.parseInt(void 0!==o?null==(t=null!=o?o:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),R=t.useMemo(()=>!K.includes(v)&&I>=100?"success":v||"normal",[v,I]),{getPrefixCls:A,direction:W,progress:X}=t.useContext(c.ConfigContext),D=A("progress",g),[L,_,V]=H(D),q="line"===k,U=q&&!f,Q=t.useMemo(()=>{let r;if(!x)return null;let s=T(e),c=y||(e=>`${e}%`),d=q&&M&&"inner"===N;return"inner"===N||y||"exception"!==R&&"success"!==R?r=c(O(b),O(s)):"exception"===R?r=q?t.createElement(i.default,null):t.createElement(a.default,null):"success"===R&&(r=q?t.createElement(o.default,null):t.createElement(n.default,null)),t.createElement("span",{className:(0,l.default)(`${D}-text`,{[`${D}-text-bright`]:d,[`${D}-text-${E}`]:U,[`${D}-text-${N}`]:U}),title:"string"==typeof r?r:void 0},r)},[x,b,I,R,k,D,y]);"line"===k?u=f?t.createElement(Y,Object.assign({},e,{strokeColor:z,prefixCls:D,steps:"object"==typeof f?f.count:f}),Q):t.createElement(F,Object.assign({},e,{strokeColor:B,prefixCls:D,direction:W,percentPosition:{align:E,type:N}}),Q):("circle"===k||"dashboard"===k)&&(u=t.createElement(j,Object.assign({},e,{strokeColor:B,prefixCls:D,progressStatus:R}),Q));let Z=(0,l.default)(D,`${D}-status-${R}`,{[`${D}-${"dashboard"===k&&"circle"||k}`]:"line"!==k,[`${D}-inline-circle`]:"circle"===k&&P(C,"circle")[0]<=20,[`${D}-line`]:U,[`${D}-line-align-${E}`]:U,[`${D}-line-position-${N}`]:U,[`${D}-steps`]:f,[`${D}-show-info`]:x,[`${D}-${C}`]:"string"==typeof C,[`${D}-rtl`]:"rtl"===W},null==X?void 0:X.className,m,p,_,V);return L(t.createElement("div",Object.assign({ref:d,style:Object.assign(Object.assign({},null==X?void 0:X.style),$),className:Z,role:"progressbar","aria-valuenow":I,"aria-valuemin":0,"aria-valuemax":100},(0,s.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),u))});e.s(["default",0,V],309821)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02eah8_db3ldv.js b/litellm/proxy/_experimental/out/_next/static/chunks/02eah8_db3ldv.js deleted file mode 100644 index 74495e8e91e..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/02eah8_db3ldv.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var l=e.i(271645);let s=l.forwardRef(function(e,s){return l.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),l.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,s],250980)},695411,e=>{"use strict";var l=e.i(602869);let s=async e=>{try{let s=await (0,l.modelHubCall)(e);if(s?.data.length>0){let e=s.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,l)=>e.model_group.localeCompare(l.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,s])},841947,e=>{"use strict";let l=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,l])},603908,e=>{"use strict";let l=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,l])},107233,e=>{"use strict";var l=e.i(603908);e.s(["Plus",()=>l.default])},37727,e=>{"use strict";var l=e.i(841947);e.s(["X",()=>l.default])},158392,63209,e=>{"use strict";var l=e.i(843476),s=e.i(311451);let t={ttl:3600,lowest_latency_buffer:0},r=({routingStrategyArgs:e})=>{let r={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsxs)("div",{className:"max-w-3xl",children:[(0,l.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||t).map(([e,t])=>(0,l.jsx)("div",{className:"space-y-2",children:(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r[e]||""}),(0,l.jsx)(s.Input,{name:e,defaultValue:"object"==typeof t?JSON.stringify(t,null,2):t?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,l.jsx)("div",{className:"border-t border-gray-200"})]})},a=({routerSettings:e,routerFieldsMetadata:t})=>(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsxs)("div",{className:"max-w-3xl",children:[(0,l.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,r])=>(0,l.jsx)("div",{className:"space-y-2",children:(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:t[e]?.ui_field_name||e}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:t[e]?.field_description||""}),(0,l.jsx)(s.Input,{name:e,defaultValue:null==r||"null"===r?"":"object"==typeof r?JSON.stringify(r,null,2):r?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var i=e.i(199133);let n=({selectedStrategy:e,availableStrategies:s,routingStrategyDescriptions:t,routerFieldsMetadata:r,onStrategyChange:a})=>(0,l.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r.routing_strategy?.field_description||""})]}),(0,l.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,l.jsx)(i.Select,{value:e,onChange:a,style:{width:"100%"},size:"large",children:s.map(e=>(0,l.jsx)(i.Select.Option,{value:e,label:e,children:(0,l.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,l.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),t[e]&&(0,l.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:t[e]})]})},e))})})]});var o=e.i(790848);let c=({enabled:e,routerFieldsMetadata:s,onToggle:t})=>(0,l.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,l.jsxs)("div",{className:"flex items-start justify-between",children:[(0,l.jsxs)("div",{className:"flex-1",children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:s.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[s.enable_tag_filtering?.field_description||"",s.enable_tag_filtering?.link&&(0,l.jsxs)(l.Fragment,{children:[" ",(0,l.jsx)("a",{href:s.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,l.jsx)(o.Switch,{checked:e,onChange:t,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:s,routerFieldsMetadata:t,availableRoutingStrategies:i,routingStrategyDescriptions:o})=>(0,l.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsxs)("div",{className:"max-w-3xl",children:[(0,l.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),i.length>0&&(0,l.jsx)(n,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:i,routingStrategyDescriptions:o,routerFieldsMetadata:t,onStrategyChange:l=>{s({...e,selectedStrategy:l})}}),(0,l.jsx)(c,{enabled:e.enableTagFiltering,routerFieldsMetadata:t,onToggle:l=>{s({...e,enableTagFiltering:l})}})]}),(0,l.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,l.jsx)(r,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,l.jsx)(a,{routerSettings:e.routerSettings,routerFieldsMetadata:t})]})],158392);var d=e.i(361653);e.s(["AlertCircle",()=>d.default],63209)},425063,e=>{"use strict";let l=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,l],425063)},419470,e=>{"use strict";var l=e.i(843476),s=e.i(994388),t=e.i(653496),r=e.i(107233),a=e.i(271645),i=e.i(888259),n=e.i(199133),o=e.i(592968),c=e.i(63209),d=e.i(425063),m=e.i(37727);function u({group:e,onChange:s,availableModels:t,maxFallbacks:r}){let a=t.filter(l=>l!==e.primaryModel),i=e.fallbackModels.length{let t=[...e.fallbackModels];t.includes(l)&&(t=t.filter(e=>e!==l)),s({...e,primaryModel:l,fallbackModels:t})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,l)=>(l?.label??"").toLowerCase().includes(e.toLowerCase()),options:t.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,l.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,l.jsx)(c.AlertCircle,{className:"w-4 h-4"}),(0,l.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,l.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,l.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,l.jsx)(d.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,l.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,l.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,l.jsx)("span",{className:"text-red-500",children:"*"}),(0,l.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",r," fallbacks at a time)"]})]}),(0,l.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,l.jsxs)("div",{className:"mb-4",children:[(0,l.jsx)(n.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:i?"Select fallback models to add...":`Maximum ${r} fallbacks reached`,value:e.fallbackModels,onChange:l=>{let t=l.slice(0,r);s({...e,fallbackModels:t})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:a.map(e=>({label:e,value:e})),optionRender:(s,t)=>{let r=e.fallbackModels.includes(s.value),a=r?e.fallbackModels.indexOf(s.value)+1:null;return(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[r&&null!==a&&(0,l.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:a}),(0,l.jsx)("span",{children:s.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,l.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,l.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,l)=>(l?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:i?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${r} used)`:`Maximum ${r} fallbacks reached. Remove some to add more.`})]}),(0,l.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,l.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,l.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,l.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((t,r)=>(0,l.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,l.jsx)("span",{className:"text-xs font-bold",children:r+1})}),(0,l.jsx)("div",{children:(0,l.jsx)("span",{className:"font-medium text-gray-800",children:t})})]}),(0,l.jsx)("button",{type:"button",onClick:()=>{let l;return l=e.fallbackModels.filter((e,l)=>l!==r),void s({...e,fallbackModels:l})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,l.jsx)(m.X,{className:"w-4 h-4"})})]},`${t}-${r}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:n,availableModels:o,maxFallbacks:c=10,maxGroups:d=5}){let[m,g]=(0,a.useState)(e.length>0?e[0].id:"1");(0,a.useEffect)(()=>{e.length>0?e.some(e=>e.id===m)||g(e[0].id):g("1")},[e]);let p=()=>{if(e.length>=d)return;let l=Date.now().toString();n([...e,{id:l,primaryModel:null,fallbackModels:[]}]),g(l)},h=l=>{n(e.map(e=>e.id===l.id?l:e))},x=e.map((s,t)=>{let r=s.primaryModel?s.primaryModel:`Group ${t+1}`;return{key:s.id,label:r,closable:e.length>1,children:(0,l.jsx)(u,{group:s,onChange:h,availableModels:o,maxFallbacks:c})}});return 0===e.length?(0,l.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,l.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,l.jsx)(s.Button,{variant:"primary",onClick:p,icon:()=>(0,l.jsx)(r.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,l.jsx)(t.Tabs,{type:"editable-card",activeKey:m,onChange:g,onEdit:(l,s)=>{"add"===s?p():"remove"===s&&e.length>1&&(l=>{if(1===e.length)return i.default.warning("At least one group is required");let s=e.filter(e=>e.id!==l);n(s),m===l&&s.length>0&&g(s[s.length-1].id)})(l)},items:x,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=d})}],419470)},246349,e=>{"use strict";let l=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,l])},992619,e=>{"use strict";var l=e.i(843476),s=e.i(271645),t=e.i(779241),r=e.i(599724),a=e.i(199133),i=e.i(983561),n=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:m=!1,style:u,className:g,showLabel:p=!0,labelText:h="Select Model"})=>{let[x,f]=(0,s.useState)(o),[y,b]=(0,s.useState)(!1),[v,j]=(0,s.useState)([]),w=(0,s.useRef)(null);return(0,s.useEffect)(()=>{f(o)},[o]),(0,s.useEffect)(()=>{e&&(async()=>{try{let l=await (0,n.fetchAvailableModels)(e);l.length>0&&j(l)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,l.jsxs)("div",{children:[p&&(0,l.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,l.jsx)(i.RobotOutlined,{className:"mr-2"})," ",h]}),(0,l.jsx)(a.Select,{value:x,placeholder:c,onChange:e=>{"custom"===e?(b(!0),f(void 0)):(b(!1),f(e),d&&d(e))},options:[...Array.from(new Set(v.map(e=>e.model_group))).map((e,l)=>({value:e,label:e,key:l})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...u},showSearch:!0,className:`rounded-md ${g||""}`,disabled:m}),y&&(0,l.jsx)(t.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{w.current&&clearTimeout(w.current),w.current=setTimeout(()=>{f(e),d&&d(e)},500)},disabled:m})]})}])},91739,e=>{"use strict";var l=e.i(544195);e.s(["Radio",()=>l.default])},988297,e=>{"use strict";var l=e.i(271645);let s=l.forwardRef(function(e,s){return l.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),l.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,s],988297)},797672,e=>{"use strict";var l=e.i(271645);let s=l.forwardRef(function(e,s){return l.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),l.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,s],797672)},361653,e=>{"use strict";let l=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,l])},409797,e=>{"use strict";var l=e.i(631171);e.s(["ChevronDownIcon",()=>l.default])},531516,696609,e=>{"use strict";var l=e.i(843476),s=e.i(271645),t=e.i(536916),r=e.i(599724),a=e.i(409797),i=e.i(246349),i=i;let n=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function m(e,l=""){let s=e.toLowerCase();if(d.test(s))return"read";if(n.test(s))return"delete";if(c.test(s))return"update";if(o.test(s))return"create";if(l){let e=l.toLowerCase();if(d.test(e))return"read";if(n.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function u(e){let l={read:[],create:[],update:[],delete:[],unknown:[]};for(let s of e)l[m(s.name,s.description)].push(s);return l}let g={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,g,"classifyToolOp",0,m,"groupToolsByCrud",0,u],696609);let p=["read","create","update","delete","unknown"],h={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},x={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},f={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:n,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[m,y]=(0,s.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),b=(0,s.useMemo)(()=>u(e),[e]),v=(0,s.useMemo)(()=>new Set(void 0===n?e.map(e=>e.name):n),[n,e]),j=e=>{if(c)return;let l=new Set(v);l.has(e)?l.delete(e):l.add(e),o(Array.from(l))};return 0===e.length?null:(0,l.jsx)("div",{className:"space-y-3",children:p.map(e=>{let s,n=b[e];if(0===n.length)return null;if(d){let e=d.toLowerCase();if(!n.some(l=>l.name.toLowerCase().includes(e)||(l.description??"").toLowerCase().includes(e)))return null}let u=g[e],p=(s=b[e]).length>0&&s.every(e=>v.has(e.name)),w=(e=>{let l=b[e];if(0===l.length)return!1;let s=l.filter(e=>v.has(e.name)).length;return s>0&&s{y(l=>({...l,[e]:!l[e]}))},children:[N?(0,l.jsx)(i.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,l.jsx)(a.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,l.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:u.label}),(0,l.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${h[u.risk]}`,children:"high"===u.risk?"High Risk":"medium"===u.risk?"Medium Risk":"low"===u.risk?"Safe":"Unclassified"}),(0,l.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[n.filter(e=>v.has(e.name)).length,"/",n.length," allowed"]})]}),!c&&(0,l.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,l.jsx)(r.Text,{className:"text-xs text-gray-500",children:p?"All on":w?"Partial":"All off"}),(0,l.jsx)(t.Checkbox,{checked:p,indeterminate:w,onChange:l=>((e,l)=>{if(c)return;let s=new Set(v);for(let t of b[e])l?s.add(t.name):s.delete(t.name);o(Array.from(s))})(e,l.target.checked),onClick:e=>e.stopPropagation()})]})]}),!N&&(0,l.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:u.description}),!N&&(0,l.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:n.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let s,a=(s=e.name,v.has(s));return(0,l.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${a?"":"opacity-60"}`,onClick:()=>j(e.name),children:[(0,l.jsx)(t.Checkbox,{checked:a,onChange:()=>j(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsx)(r.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,l.jsx)(r.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,l.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${a?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:a?"on":"off"})]},e.name)})})]},e)})})}],531516)},309426,e=>{"use strict";var l=e.i(290571),s=e.i(444755),t=e.i(673706),r=e.i(271645),a=e.i(46757);let i=(0,t.makeClassName)("Col"),n=r.default.forwardRef((e,t)=>{let n,o,c,d,{numColSpan:m=1,numColSpanSm:u,numColSpanMd:g,numColSpanLg:p,children:h,className:x}=e,f=(0,l.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),y=(e,l)=>e&&Object.keys(l).includes(String(e))?l[e]:"";return r.default.createElement("div",Object.assign({ref:t,className:(0,s.tremorTwMerge)(i("root"),(n=y(m,a.colSpan),o=y(u,a.colSpanSm),c=y(g,a.colSpanMd),d=y(p,a.colSpanLg),(0,s.tremorTwMerge)(n,o,c,d)),x)},f),h)});n.displayName="Col",e.s(["Col",0,n],309426)},213205,e=>{"use strict";e.i(247167);var l=e.i(931067),s=e.i(271645);let t={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var r=e.i(9583),a=s.forwardRef(function(e,a){return s.createElement(r.default,(0,l.default)({},e,{ref:a,icon:t}))});e.s(["UserAddOutlined",0,a],213205)},355619,e=>{"use strict";var l=e.i(602869);let s=async(e,s,t)=>{try{if(null===e||null===s)return;if(null!==t){let r=(await (0,l.modelAvailableCall)(t,e,s,!0,null,!0)).data.map(e=>e.id),a=[],i=[];return r.forEach(e=>{e.endsWith("/*")?a.push(e):i.push(e)}),[...a,...i]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["fetchAvailableModelsForTeamOrKey",0,s,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let l=e.replace("/*","");return`All ${l} models`}return e},"unfurlWildcardModelsInList",0,(e,l)=>{let s=[],t=[];return e.forEach(e=>{if(e.endsWith("/*")){let r=e.replace("/*",""),a=l.filter(e=>e.startsWith(r+"/"));t.push(...a),s.push(e)}else t.push(e)}),[...s,...t].filter((e,l,s)=>s.indexOf(e)===l)}])},860585,e=>{"use strict";var l=e.i(843476),s=e.i(199133);let{Option:t}=s.Select;e.s(["default",0,({value:e,onChange:r,className:a="",style:i={}})=>(0,l.jsxs)(s.Select,{style:{width:"100%",...i},value:e||void 0,onChange:r,className:a,placeholder:"n/a",allowClear:!0,children:[(0,l.jsx)(t,{value:"1h",children:"hourly"}),(0,l.jsx)(t,{value:"24h",children:"daily"}),(0,l.jsx)(t,{value:"7d",children:"weekly"}),(0,l.jsx)(t,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},350967,46757,e=>{"use strict";var l=e.i(290571),s=e.i(444755),t=e.i(673706),r=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},i={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},n={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},o={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,o,"gridColsMd",0,n,"gridColsSm",0,i],46757);let c=(0,t.makeClassName)("Grid"),d=(e,l)=>e&&Object.keys(l).includes(String(e))?l[e]:"",m=r.default.forwardRef((e,t)=>{let{numItems:m=1,numItemsSm:u,numItemsMd:g,numItemsLg:p,children:h,className:x}=e,f=(0,l.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),y=d(m,a),b=d(u,i),v=d(g,n),j=d(p,o),w=(0,s.tremorTwMerge)(y,b,v,j);return r.default.createElement("div",Object.assign({ref:t,className:(0,s.tremorTwMerge)(c("root"),"grid",w,x)},f),h)});m.displayName="Grid",e.s(["Grid",0,m],350967)},981339,e=>{"use strict";var l=e.i(185793);e.s(["Skeleton",()=>l.default])},500727,e=>{"use strict";var l=e.i(266027),s=e.i(243652),t=e.i(602869),r=e.i(135214);let a=(0,s.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:s}=(0,r.default)();return(0,l.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,t.fetchMCPServers)(s,e),enabled:!!s})}])},699857,e=>{"use strict";var l=e.i(266027),s=e.i(243652),t=e.i(602869),r=e.i(135214);let a=(0,s.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,r.default)();return(0,l.useQuery)({queryKey:a.list(),queryFn:async()=>await (0,t.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var l=e.i(843476),s=e.i(271645),t=e.i(199133),r=e.i(602869);e.s(["default",0,({onChange:e,value:a,className:i,accessToken:n,placeholder:o="Select vector stores",disabled:c=!1})=>{let[d,m]=(0,s.useState)([]),[u,g]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,r.vectorStoreListCall)(n);e.data&&m(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{g(!1)}}})()},[n]),(0,l.jsx)("div",{children:(0,l.jsx)(t.Select,{mode:"multiple",placeholder:o,onChange:e,value:a,loading:u,className:i,allowClear:!0,options:d.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:c})})}])},75921,e=>{"use strict";var l=e.i(843476),s=e.i(266027),t=e.i(243652),r=e.i(602869),a=e.i(135214);let i=(0,t.createQueryKeys)("mcpAccessGroups");var n=e.i(500727),o=e.i(699857),c=e.i(199133),d=e.i(234713);let m="toolset:";e.s(["default",0,({onChange:e,value:t,className:u,accessToken:g,placeholder:p="Select MCP servers",disabled:h=!1,teamId:x,allowNoMcpServers:f=!1,allowAllProxyMcpServers:y=!1})=>{let{data:b=[],isLoading:v}=(0,n.useMCPServers)(x),{data:j=[],isLoading:w}=(()=>{let{accessToken:e}=(0,a.default)();return(0,s.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,r.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:N=[],isLoading:S}=(0,o.useMCPToolsets)(),k=new Set(j),_=[...j.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...b.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...N.map(e=>({label:e.toolset_name,value:`${m}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],C={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...t?.servers||[],...t?.accessGroups||[],...(t?.toolsets||[]).map(e=>`${m}${e}`)],L=f&&E.includes(d.NO_MCP_SERVERS_SENTINEL),R=E.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,l.jsx)("div",{children:(0,l.jsxs)(c.Select,{mode:"multiple",placeholder:p,onChange:l=>{if(y&&l.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[d.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(f&&l.includes(d.NO_MCP_SERVERS_SENTINEL))return void e({servers:[d.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let s=l.filter(e=>e.startsWith(m)).map(e=>e.slice(m.length)),t=l.filter(e=>!e.startsWith(m));e({servers:t.filter(e=>!k.has(e)),accessGroups:t.filter(e=>k.has(e)),toolsets:s})},value:E,loading:v||w||S,className:u,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:h,filterOption:(e,l)=>l?.value===d.NO_MCP_SERVERS_SENTINEL||l?.value===d.ALL_PROXY_MCP_SERVERS_SENTINEL||(_.find(e=>e.value===l?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(y||R)&&(0,l.jsx)(c.Select.Option,{value:d.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,l.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},d.ALL_PROXY_MCP_SERVERS_SENTINEL),f&&(0,l.jsx)(c.Select.Option,{value:d.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,l.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,l.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},d.NO_MCP_SERVERS_SENTINEL),_.map(e=>(0,l.jsx)(c.Select.Option,{value:e.value,label:e.label,disabled:L||R,children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,l.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:C[e.type],flexShrink:0}}),(0,l.jsx)("span",{style:{flex:1},children:e.label}),(0,l.jsx)("span",{style:{color:C[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02hq_0zk6htur.js b/litellm/proxy/_experimental/out/_next/static/chunks/02hq_0zk6htur.js new file mode 100644 index 00000000000..fb07f00e01b --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02hq_0zk6htur.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,s=(e,t,r,o,a)=>{clearTimeout(o.current);let n=l(e);t(n),r.current=n,a&&a({current:n})};var i=e.i(480731),c=e.i(444755),d=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,d.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,c.tremorTwMerge)((0,d.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,d.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:a,needMargin:l,transitionStatus:n})=>{let s=l?r===i.HorizontalPositions.Left?(0,c.tremorTwMerge)("-ml-1","mr-1.5"):(0,c.tremorTwMerge)("-mr-1","ml-1.5"):"",d=(0,c.tremorTwMerge)("w-0 h-0"),m={default:d,entering:d,entered:t,exiting:t,exited:d};return e?o.default.createElement(u,{className:(0,c.tremorTwMerge)(f("icon"),"animate-spin shrink-0",s,m.default,m[n]),style:{transition:"width 150ms"}}):o.default.createElement(a,{className:(0,c.tremorTwMerge)(f("icon"),"shrink-0",t,s)})},b=o.default.forwardRef((e,a)=>{let{icon:u,iconPosition:m=i.HorizontalPositions.Left,size:b=i.Sizes.SM,color:v,variant:C="primary",disabled:x,loading:k=!1,loadingText:y,children:w,tooltip:$,className:S}=e,E=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),N=k||x,T=void 0!==u||k,B=k&&y,M=!(!w&&!B),z=(0,c.tremorTwMerge)(g[b].height,g[b].width),j="light"!==C?(0,c.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",P=p(C,v),O=("light"!==C?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:R,getReferenceProps:I}=(0,r.useTooltip)(300),[A,L]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:a,timeout:i,initialEntered:c,mountOnEnter:d,unmountOnExit:u,onStateChange:m}={})=>{let[g,p]=(0,o.useState)(()=>l(c?2:n(d))),f=(0,o.useRef)(g),h=(0,o.useRef)(0),[b,v]="object"==typeof i?[i.enter,i.exit]:[i,i],C=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(f.current._s,u);e&&s(e,p,f,h,m)},[m,u]);return[g,(0,o.useCallback)(o=>{let l=e=>{switch(s(e,p,f,h,m),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(C,b));break;case 4:v>=0&&(h.current=((...e)=>setTimeout(...e))(C,v));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},i=f.current.isEnter;"boolean"!=typeof o&&(o=!i),o?i||l(e?+!r:2):i&&l(t?a?3:4:n(u))},[C,m,e,t,r,a,b,v,u]),C]})({timeout:50});return(0,o.useEffect)(()=>{L(k)},[k]),o.default.createElement("button",Object.assign({ref:(0,d.mergeRefs)([a,R.refs.setReference]),className:(0,c.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",j,O.paddingX,O.paddingY,O.fontSize,P.textColor,P.bgColor,P.borderColor,P.hoverBorderColor,N?"opacity-50 cursor-not-allowed":(0,c.tremorTwMerge)(p(C,v).hoverTextColor,p(C,v).hoverBgColor,p(C,v).hoverBorderColor),S),disabled:N},I,E),o.default.createElement(r.default,Object.assign({text:$},R)),T&&m!==i.HorizontalPositions.Right?o.default.createElement(h,{loading:k,iconSize:z,iconPosition:m,Icon:u,transitionStatus:A.status,needMargin:M}):null,B||w?o.default.createElement("span",{className:(0,c.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},B?y:w):null,T&&m===i.HorizontalPositions.Right?o.default.createElement(h,{loading:k,iconSize:z,iconPosition:m,Icon:u,transitionStatus:A.status,needMargin:M}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),o=e.i(673706),a=e.i(271645);let l=a.default.forwardRef((e,l)=>{let{color:n,className:s,children:i}=e;return a.default.createElement("p",{ref:l,className:(0,r.tremorTwMerge)("text-tremor-default",n?(0,o.getColorClassNames)(n,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),s)},i)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),a=e.i(95779),l=e.i(444755),n=e.i(673706);let s=(0,n.makeClassName)("Card"),i=r.default.forwardRef((e,i)=>{let{decoration:c="",decorationColor:d,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:i,className:(0,l.tremorTwMerge)(s("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",d?(0,n.getColorClassNames)(d,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(c),m)},g),u)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),a=e.i(673706),l=e.i(271645);let n=l.default.forwardRef((e,n)=>{let{color:s,children:i,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return l.default.createElement("p",Object.assign({ref:n,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",s?(0,a.getColorClassNames)(s,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",c)},d),i)});n.displayName="Title",e.s(["Title",0,n],629569)},350967,46757,e=>{"use strict";var t=e.i(290571),r=e.i(444755),o=e.i(673706),a=e.i(271645);let l={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},n={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},s={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,l,"gridColsLg",0,i,"gridColsMd",0,s,"gridColsSm",0,n],46757);let c=(0,o.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=a.default.forwardRef((e,o)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:p,children:f,className:h}=e,b=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),v=d(u,l),C=d(m,n),x=d(g,s),k=d(p,i),y=(0,r.tremorTwMerge)(v,C,x,k);return a.default.createElement("div",Object.assign({ref:o,className:(0,r.tremorTwMerge)(c("root"),"grid",y,h)},b),f)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["RobotOutlined",0,l],983561)},797672,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,r],797672)},992619,e=>{"use strict";var t=e.i(843476),r=e.i(271645),o=e.i(779241),a=e.i(599724),l=e.i(199133),n=e.i(983561),s=e.i(695411);e.s(["default",0,({accessToken:e,value:i,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:g,showLabel:p=!0,labelText:f="Select Model"})=>{let[h,b]=(0,r.useState)(i),[v,C]=(0,r.useState)(!1),[x,k]=(0,r.useState)([]),y=(0,r.useRef)(null);return(0,r.useEffect)(()=>{b(i)},[i]),(0,r.useEffect)(()=>{e&&(async()=>{try{let t=await (0,s.fetchAvailableModels)(e);t.length>0&&k(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[p&&(0,t.jsxs)(a.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(n.RobotOutlined,{className:"mr-2"})," ",f]}),(0,t.jsx)(l.Select,{value:h,placeholder:c,onChange:e=>{"custom"===e?(C(!0),b(void 0)):(C(!1),b(e),d&&d(e))},options:[...Array.from(new Set(x.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${g||""}`,disabled:u}),v&&(0,t.jsx)(o.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{y.current&&clearTimeout(y.current),y.current=setTimeout(()=>{b(e),d&&d(e)},500)},disabled:u})]})}])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),o=e.i(201072),a=e.i(121229),l=e.i(726289),n=e.i(864517),s=e.i(343794),i=e.i(529681),c=e.i(242064),d=e.i(931067),u=e.i(209428),m=e.i(703923),g={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},p=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),o=!1;e.current.forEach(function(e){if(e){o=!0;var a=e.style;a.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(a.transitionDuration="0s, 0s")}}),o&&(r.current=Date.now())}),e.current},f=e.i(410160),h=e.i(392221),b=e.i(654310),v=0,C=(0,b.default)();let x=function(e){var r=t.useState(),o=(0,h.default)(r,2),a=o[0],l=o[1];return t.useEffect(function(){var e;l("rc_progress_".concat((C?(e=v,v+=1):e="TEST_OR_SSR",e)))},[]),e||a};var k=function(e){var r=e.bg,o=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},o)};function y(e,t){return Object.keys(e).map(function(r){var o=parseFloat(r),a="".concat(Math.floor(o*t),"%");return"".concat(e[r]," ").concat(a)})}var w=t.forwardRef(function(e,r){var o=e.prefixCls,a=e.color,l=e.gradientId,n=e.radius,s=e.style,i=e.ptg,c=e.strokeLinecap,d=e.strokeWidth,u=e.size,m=e.gapDegree,g=a&&"object"===(0,f.default)(a),p=u/2,h=t.createElement("circle",{className:"".concat(o,"-circle-path"),r:n,cx:p,cy:p,stroke:g?"#FFF":void 0,strokeLinecap:c,strokeWidth:d,opacity:+(0!==i),style:s,ref:r});if(!g)return h;var b="".concat(l,"-conic"),v=y(a,(360-m)/360),C=y(a,1),x="conic-gradient(from ".concat(m?"".concat(180+m/2,"deg"):"0deg",", ").concat(v.join(", "),")"),w="linear-gradient(to ".concat(m?"bottom":"top",", ").concat(C.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:u,height:u,mask:"url(#".concat(b,")")},t.createElement(k,{bg:w},t.createElement(k,{bg:x}))))}),$=function(e,t,r,o,a,l,n,s,i,c){var d=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,u=(100-o)/100*t;return"round"===i&&100!==o&&(u+=c/2)>=t&&(u=t-.01),{stroke:"string"==typeof s?s:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:u+d,transform:"rotate(".concat(a+r/100*360*((360-l)/360)+(0===l?0:({bottom:0,top:180,left:90,right:-90})[n]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let N=function(e){var r,o,a,l,n=(0,u.default)((0,u.default)({},g),e),i=n.id,c=n.prefixCls,h=n.steps,b=n.strokeWidth,v=n.trailWidth,C=n.gapDegree,k=void 0===C?0:C,y=n.gapPosition,N=n.trailColor,T=n.strokeLinecap,B=n.style,M=n.className,z=n.strokeColor,j=n.percent,P=(0,m.default)(n,S),O=x(i),R="".concat(O,"-gradient"),I=50-b/2,A=2*Math.PI*I,L=k>0?90+k/2:-90,H=(360-k)/360*A,X="object"===(0,f.default)(h)?h:{count:h,gap:2},D=X.count,W=X.gap,_=E(j),F=E(z),V=F.find(function(e){return e&&"object"===(0,f.default)(e)}),Y=V&&"object"===(0,f.default)(V)?"butt":T,G=$(A,H,0,100,L,k,y,N,Y,b),K=p();return t.createElement("svg",(0,d.default)({className:(0,s.default)("".concat(c,"-circle"),M),viewBox:"0 0 ".concat(100," ").concat(100),style:B,id:i,role:"presentation"},P),!D&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:I,cx:50,cy:50,stroke:N,strokeLinecap:Y,strokeWidth:v||b,style:G}),D?(r=Math.round(D*(_[0]/100)),o=100/D,a=0,Array(D).fill(null).map(function(e,l){var n=l<=r-1?F[0]:N,s=n&&"object"===(0,f.default)(n)?"url(#".concat(R,")"):void 0,i=$(A,H,a,o,L,k,y,n,"butt",b,W);return a+=(H-i.strokeDashoffset+W)*100/H,t.createElement("circle",{key:l,className:"".concat(c,"-circle-path"),r:I,cx:50,cy:50,stroke:s,strokeWidth:b,opacity:1,style:i,ref:function(e){K[l]=e}})})):(l=0,_.map(function(e,r){var o=F[r]||F[F.length-1],a=$(A,H,l,e,L,k,y,o,Y,b);return l+=e,t.createElement(w,{key:r,color:o,ptg:e,radius:I,prefixCls:c,gradientId:R,style:a,strokeLinecap:Y,strokeWidth:b,gapDegree:k,ref:function(e){K[r]=e},size:100})}).reverse()))};var T=e.i(491816);e.i(765846);var B=e.i(896091);function M(e){return!e||e<0?0:e>100?100:e}function z({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let j=(e,t,r)=>{var o,a,l,n;let s=-1,i=-1;if("step"===t){let t=r.steps,o=r.strokeWidth;"string"==typeof e||void 0===e?(s="small"===e?2:14,i=null!=o?o:8):"number"==typeof e?[s,i]=[e,e]:[s=14,i=8]=Array.isArray(e)?e:[e.width,e.height],s*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?i=t||("small"===e?6:8):"number"==typeof e?[s,i]=[e,e]:[s=-1,i=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[s,i]="small"===e?[60,60]:[120,120]:"number"==typeof e?[s,i]=[e,e]:Array.isArray(e)&&(s=null!=(a=null!=(o=e[0])?o:e[1])?a:120,i=null!=(n=null!=(l=e[0])?l:e[1])?n:120));return[s,i]},P=e=>{let{prefixCls:r,trailColor:o=null,strokeLinecap:a="round",gapPosition:l,gapDegree:n,width:i=120,type:c,children:d,success:u,size:m=i,steps:g}=e,[p,f]=j(m,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/p*100,6));let b=t.useMemo(()=>n||0===n?n:"dashboard"===c?75:void 0,[n,c]),v=(({percent:e,success:t,successPercent:r})=>{let o=M(z({success:t,successPercent:r}));return[o,M(M(e)-o)]})(e),C="[object Object]"===Object.prototype.toString.call(e.strokeColor),x=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||B.presetPrimaryColors.green,t||null]})({success:u,strokeColor:e.strokeColor}),k=(0,s.default)(`${r}-inner`,{[`${r}-circle-gradient`]:C}),y=t.createElement(N,{steps:g,percent:g?v[1]:v,strokeWidth:h,trailWidth:h,strokeColor:g?x[1]:x,strokeLinecap:a,trailColor:o,prefixCls:r,gapDegree:b,gapPosition:l||"dashboard"===c&&"bottom"||void 0}),w=p<=20,$=t.createElement("div",{className:k,style:{width:p,height:f,fontSize:.15*p+6}},y,!w&&d);return w?t.createElement(T.default,{title:d},$):$};e.i(296059);var O=e.i(694758),R=e.i(915654),I=e.i(183293),A=e.i(246422),L=e.i(838378);let H="--progress-line-stroke-color",X="--progress-percent",D=e=>{let t=e?"100%":"-100%";return new O.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},W=(0,A.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,L.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,I.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${H})`]},height:"100%",width:`calc(1 / var(${X}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,R.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:D(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:D(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var _=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let F=e=>{let{prefixCls:r,direction:o,percent:a,size:l,strokeWidth:n,strokeColor:i,strokeLinecap:c="round",children:d,trailColor:u=null,percentPosition:m,success:g}=e,{align:p,type:f}=m,h=i&&"string"!=typeof i?((e,t)=>{let{from:r=B.presetPrimaryColors.blue,to:o=B.presetPrimaryColors.blue,direction:a="rtl"===t?"to left":"to right"}=e,l=_(e,["from","to","direction"]);if(0!==Object.keys(l).length){let e,t=(e=[],Object.keys(l).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:l[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${a}, ${t})`;return{background:r,[H]:r}}let n=`linear-gradient(${a}, ${r}, ${o})`;return{background:n,[H]:n}})(i,o):{[H]:i,background:i},b="square"===c||"butt"===c?0:void 0,[v,C]=j(null!=l?l:[-1,n||("small"===l?6:8)],"line",{strokeWidth:n}),x=Object.assign(Object.assign({width:`${M(a)}%`,height:C,borderRadius:b},h),{[X]:M(a)/100}),k=z(e),y={width:`${M(k)}%`,height:C,borderRadius:b,backgroundColor:null==g?void 0:g.strokeColor},w=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:u||void 0,borderRadius:b}},t.createElement("div",{className:(0,s.default)(`${r}-bg`,`${r}-bg-${f}`),style:x},"inner"===f&&d),void 0!==k&&t.createElement("div",{className:`${r}-success-bg`,style:y})),$="outer"===f&&"start"===p,S="outer"===f&&"end"===p;return"outer"===f&&"center"===p?t.createElement("div",{className:`${r}-layout-bottom`},w,d):t.createElement("div",{className:`${r}-outer`,style:{width:v<0?"100%":v}},$&&d,w,S&&d)},V=e=>{let{size:r,steps:o,rounding:a=Math.round,percent:l=0,strokeWidth:n=8,strokeColor:i,trailColor:c=null,prefixCls:d,children:u}=e,m=a(l/100*o),[g,p]=j(null!=r?r:["small"===r?2:14,n],"step",{steps:o,strokeWidth:n}),f=g/o,h=Array.from({length:o});for(let e=0;et.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let G=["normal","exception","active","success"],K=t.forwardRef((e,d)=>{let u,{prefixCls:m,className:g,rootClassName:p,steps:f,strokeColor:h,percent:b=0,size:v="default",showInfo:C=!0,type:x="line",status:k,format:y,style:w,percentPosition:$={}}=e,S=Y(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:N="outer"}=$,T=Array.isArray(h)?h[0]:h,B="string"==typeof h||Array.isArray(h)?h:void 0,O=t.useMemo(()=>{if(T){let e="string"==typeof T?T:Object.values(T)[0];return new r.FastColor(e).isLight()}return!1},[h]),R=t.useMemo(()=>{var t,r;let o=z(e);return Number.parseInt(void 0!==o?null==(t=null!=o?o:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),I=t.useMemo(()=>!G.includes(k)&&R>=100?"success":k||"normal",[k,R]),{getPrefixCls:A,direction:L,progress:H}=t.useContext(c.ConfigContext),X=A("progress",m),[D,_,K]=W(X),U="line"===x,q=U&&!f,Q=t.useMemo(()=>{let r;if(!C)return null;let i=z(e),c=y||(e=>`${e}%`),d=U&&O&&"inner"===N;return"inner"===N||y||"exception"!==I&&"success"!==I?r=c(M(b),M(i)):"exception"===I?r=U?t.createElement(l.default,null):t.createElement(n.default,null):"success"===I&&(r=U?t.createElement(o.default,null):t.createElement(a.default,null)),t.createElement("span",{className:(0,s.default)(`${X}-text`,{[`${X}-text-bright`]:d,[`${X}-text-${E}`]:q,[`${X}-text-${N}`]:q}),title:"string"==typeof r?r:void 0},r)},[C,b,R,I,x,X,y]);"line"===x?u=f?t.createElement(V,Object.assign({},e,{strokeColor:B,prefixCls:X,steps:"object"==typeof f?f.count:f}),Q):t.createElement(F,Object.assign({},e,{strokeColor:T,prefixCls:X,direction:L,percentPosition:{align:E,type:N}}),Q):("circle"===x||"dashboard"===x)&&(u=t.createElement(P,Object.assign({},e,{strokeColor:T,prefixCls:X,progressStatus:I}),Q));let Z=(0,s.default)(X,`${X}-status-${I}`,{[`${X}-${"dashboard"===x&&"circle"||x}`]:"line"!==x,[`${X}-inline-circle`]:"circle"===x&&j(v,"circle")[0]<=20,[`${X}-line`]:q,[`${X}-line-align-${E}`]:q,[`${X}-line-position-${N}`]:q,[`${X}-steps`]:f,[`${X}-show-info`]:C,[`${X}-${v}`]:"string"==typeof v,[`${X}-rtl`]:"rtl"===L},null==H?void 0:H.className,g,p,_,K);return D(t.createElement("div",Object.assign({ref:d,style:Object.assign(Object.assign({},null==H?void 0:H.style),w),className:Z,role:"progressbar","aria-valuenow":R,"aria-valuemin":0,"aria-valuemax":100},(0,i.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),u))});e.s(["default",0,K],309821)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["default",0,l],597440)},184163,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["default",0,l],184163)},519756,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["UploadOutlined",0,l],519756)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02iqizny3-cps.js b/litellm/proxy/_experimental/out/_next/static/chunks/02iqizny3-cps.js deleted file mode 100644 index 8a139b3e0b2..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/02iqizny3-cps.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var l=e.i(271645);let s=l.forwardRef(function(e,s){return l.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),l.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,s],250980)},695411,e=>{"use strict";var l=e.i(602869);let s=async e=>{try{let s=await (0,l.modelHubCall)(e);if(s?.data.length>0){let e=s.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,l)=>e.model_group.localeCompare(l.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,s])},841947,e=>{"use strict";let l=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,l])},603908,e=>{"use strict";let l=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,l])},107233,e=>{"use strict";var l=e.i(603908);e.s(["Plus",()=>l.default])},37727,e=>{"use strict";var l=e.i(841947);e.s(["X",()=>l.default])},425063,e=>{"use strict";let l=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,l],425063)},158392,63209,e=>{"use strict";var l=e.i(843476),s=e.i(311451);let t={ttl:3600,lowest_latency_buffer:0},r=({routingStrategyArgs:e})=>{let r={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsxs)("div",{className:"max-w-3xl",children:[(0,l.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||t).map(([e,t])=>(0,l.jsx)("div",{className:"space-y-2",children:(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r[e]||""}),(0,l.jsx)(s.Input,{name:e,defaultValue:"object"==typeof t?JSON.stringify(t,null,2):t?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,l.jsx)("div",{className:"border-t border-gray-200"})]})},a=({routerSettings:e,routerFieldsMetadata:t})=>(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsxs)("div",{className:"max-w-3xl",children:[(0,l.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,r])=>(0,l.jsx)("div",{className:"space-y-2",children:(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:t[e]?.ui_field_name||e}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:t[e]?.field_description||""}),(0,l.jsx)(s.Input,{name:e,defaultValue:null==r||"null"===r?"":"object"==typeof r?JSON.stringify(r,null,2):r?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var i=e.i(199133);let n=({selectedStrategy:e,availableStrategies:s,routingStrategyDescriptions:t,routerFieldsMetadata:r,onStrategyChange:a})=>(0,l.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r.routing_strategy?.field_description||""})]}),(0,l.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,l.jsx)(i.Select,{value:e,onChange:a,style:{width:"100%"},size:"large",children:s.map(e=>(0,l.jsx)(i.Select.Option,{value:e,label:e,children:(0,l.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,l.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),t[e]&&(0,l.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:t[e]})]})},e))})})]});var o=e.i(790848);let c=({enabled:e,routerFieldsMetadata:s,onToggle:t})=>(0,l.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,l.jsxs)("div",{className:"flex items-start justify-between",children:[(0,l.jsxs)("div",{className:"flex-1",children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:s.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[s.enable_tag_filtering?.field_description||"",s.enable_tag_filtering?.link&&(0,l.jsxs)(l.Fragment,{children:[" ",(0,l.jsx)("a",{href:s.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,l.jsx)(o.Switch,{checked:e,onChange:t,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:s,routerFieldsMetadata:t,availableRoutingStrategies:i,routingStrategyDescriptions:o})=>(0,l.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsxs)("div",{className:"max-w-3xl",children:[(0,l.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),i.length>0&&(0,l.jsx)(n,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:i,routingStrategyDescriptions:o,routerFieldsMetadata:t,onStrategyChange:l=>{s({...e,selectedStrategy:l})}}),(0,l.jsx)(c,{enabled:e.enableTagFiltering,routerFieldsMetadata:t,onToggle:l=>{s({...e,enableTagFiltering:l})}})]}),(0,l.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,l.jsx)(r,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,l.jsx)(a,{routerSettings:e.routerSettings,routerFieldsMetadata:t})]})],158392);var d=e.i(361653);e.s(["AlertCircle",()=>d.default],63209)},419470,e=>{"use strict";var l=e.i(843476),s=e.i(994388),t=e.i(653496),r=e.i(107233),a=e.i(271645),i=e.i(888259),n=e.i(199133),o=e.i(592968),c=e.i(63209),d=e.i(425063),m=e.i(37727);function u({group:e,onChange:s,availableModels:t,maxFallbacks:r}){let a=t.filter(l=>l!==e.primaryModel),i=e.fallbackModels.length{let t=[...e.fallbackModels];t.includes(l)&&(t=t.filter(e=>e!==l)),s({...e,primaryModel:l,fallbackModels:t})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,l)=>(l?.label??"").toLowerCase().includes(e.toLowerCase()),options:t.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,l.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,l.jsx)(c.AlertCircle,{className:"w-4 h-4"}),(0,l.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,l.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,l.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,l.jsx)(d.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,l.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,l.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,l.jsx)("span",{className:"text-red-500",children:"*"}),(0,l.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",r," fallbacks at a time)"]})]}),(0,l.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,l.jsxs)("div",{className:"mb-4",children:[(0,l.jsx)(n.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:i?"Select fallback models to add...":`Maximum ${r} fallbacks reached`,value:e.fallbackModels,onChange:l=>{let t=l.slice(0,r);s({...e,fallbackModels:t})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:a.map(e=>({label:e,value:e})),optionRender:(s,t)=>{let r=e.fallbackModels.includes(s.value),a=r?e.fallbackModels.indexOf(s.value)+1:null;return(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[r&&null!==a&&(0,l.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:a}),(0,l.jsx)("span",{children:s.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,l.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,l.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,l)=>(l?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:i?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${r} used)`:`Maximum ${r} fallbacks reached. Remove some to add more.`})]}),(0,l.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,l.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,l.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,l.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((t,r)=>(0,l.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,l.jsx)("span",{className:"text-xs font-bold",children:r+1})}),(0,l.jsx)("div",{children:(0,l.jsx)("span",{className:"font-medium text-gray-800",children:t})})]}),(0,l.jsx)("button",{type:"button",onClick:()=>{let l;return l=e.fallbackModels.filter((e,l)=>l!==r),void s({...e,fallbackModels:l})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,l.jsx)(m.X,{className:"w-4 h-4"})})]},`${t}-${r}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:n,availableModels:o,maxFallbacks:c=10,maxGroups:d=5}){let[m,g]=(0,a.useState)(e.length>0?e[0].id:"1");(0,a.useEffect)(()=>{e.length>0?e.some(e=>e.id===m)||g(e[0].id):g("1")},[e]);let p=()=>{if(e.length>=d)return;let l=Date.now().toString();n([...e,{id:l,primaryModel:null,fallbackModels:[]}]),g(l)},h=l=>{n(e.map(e=>e.id===l.id?l:e))},x=e.map((s,t)=>{let r=s.primaryModel?s.primaryModel:`Group ${t+1}`;return{key:s.id,label:r,closable:e.length>1,children:(0,l.jsx)(u,{group:s,onChange:h,availableModels:o,maxFallbacks:c})}});return 0===e.length?(0,l.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,l.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,l.jsx)(s.Button,{variant:"primary",onClick:p,icon:()=>(0,l.jsx)(r.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,l.jsx)(t.Tabs,{type:"editable-card",activeKey:m,onChange:g,onEdit:(l,s)=>{"add"===s?p():"remove"===s&&e.length>1&&(l=>{if(1===e.length)return i.default.warning("At least one group is required");let s=e.filter(e=>e.id!==l);n(s),m===l&&s.length>0&&g(s[s.length-1].id)})(l)},items:x,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=d})}],419470)},246349,e=>{"use strict";let l=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,l])},992619,e=>{"use strict";var l=e.i(843476),s=e.i(271645),t=e.i(779241),r=e.i(599724),a=e.i(199133),i=e.i(983561),n=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:m=!1,style:u,className:g,showLabel:p=!0,labelText:h="Select Model"})=>{let[x,f]=(0,s.useState)(o),[y,b]=(0,s.useState)(!1),[v,j]=(0,s.useState)([]),w=(0,s.useRef)(null);return(0,s.useEffect)(()=>{f(o)},[o]),(0,s.useEffect)(()=>{e&&(async()=>{try{let l=await (0,n.fetchAvailableModels)(e);l.length>0&&j(l)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,l.jsxs)("div",{children:[p&&(0,l.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,l.jsx)(i.RobotOutlined,{className:"mr-2"})," ",h]}),(0,l.jsx)(a.Select,{value:x,placeholder:c,onChange:e=>{"custom"===e?(b(!0),f(void 0)):(b(!1),f(e),d&&d(e))},options:[...Array.from(new Set(v.map(e=>e.model_group))).map((e,l)=>({value:e,label:e,key:l})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...u},showSearch:!0,className:`rounded-md ${g||""}`,disabled:m}),y&&(0,l.jsx)(t.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{w.current&&clearTimeout(w.current),w.current=setTimeout(()=>{f(e),d&&d(e)},500)},disabled:m})]})}])},91739,e=>{"use strict";var l=e.i(544195);e.s(["Radio",()=>l.default])},988297,e=>{"use strict";var l=e.i(271645);let s=l.forwardRef(function(e,s){return l.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),l.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,s],988297)},797672,e=>{"use strict";var l=e.i(271645);let s=l.forwardRef(function(e,s){return l.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),l.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,s],797672)},361653,e=>{"use strict";let l=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,l])},409797,e=>{"use strict";var l=e.i(631171);e.s(["ChevronDownIcon",()=>l.default])},531516,696609,e=>{"use strict";var l=e.i(843476),s=e.i(271645),t=e.i(536916),r=e.i(599724),a=e.i(409797),i=e.i(246349),i=i;let n=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function m(e,l=""){let s=e.toLowerCase();if(d.test(s))return"read";if(n.test(s))return"delete";if(c.test(s))return"update";if(o.test(s))return"create";if(l){let e=l.toLowerCase();if(d.test(e))return"read";if(n.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function u(e){let l={read:[],create:[],update:[],delete:[],unknown:[]};for(let s of e)l[m(s.name,s.description)].push(s);return l}let g={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,g,"classifyToolOp",0,m,"groupToolsByCrud",0,u],696609);let p=["read","create","update","delete","unknown"],h={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},x={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},f={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:n,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[m,y]=(0,s.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),b=(0,s.useMemo)(()=>u(e),[e]),v=(0,s.useMemo)(()=>new Set(void 0===n?e.map(e=>e.name):n),[n,e]),j=e=>{if(c)return;let l=new Set(v);l.has(e)?l.delete(e):l.add(e),o(Array.from(l))};return 0===e.length?null:(0,l.jsx)("div",{className:"space-y-3",children:p.map(e=>{let s,n=b[e];if(0===n.length)return null;if(d){let e=d.toLowerCase();if(!n.some(l=>l.name.toLowerCase().includes(e)||(l.description??"").toLowerCase().includes(e)))return null}let u=g[e],p=(s=b[e]).length>0&&s.every(e=>v.has(e.name)),w=(e=>{let l=b[e];if(0===l.length)return!1;let s=l.filter(e=>v.has(e.name)).length;return s>0&&s{y(l=>({...l,[e]:!l[e]}))},children:[N?(0,l.jsx)(i.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,l.jsx)(a.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,l.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:u.label}),(0,l.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${h[u.risk]}`,children:"high"===u.risk?"High Risk":"medium"===u.risk?"Medium Risk":"low"===u.risk?"Safe":"Unclassified"}),(0,l.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[n.filter(e=>v.has(e.name)).length,"/",n.length," allowed"]})]}),!c&&(0,l.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,l.jsx)(r.Text,{className:"text-xs text-gray-500",children:p?"All on":w?"Partial":"All off"}),(0,l.jsx)(t.Checkbox,{checked:p,indeterminate:w,onChange:l=>((e,l)=>{if(c)return;let s=new Set(v);for(let t of b[e])l?s.add(t.name):s.delete(t.name);o(Array.from(s))})(e,l.target.checked),onClick:e=>e.stopPropagation()})]})]}),!N&&(0,l.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:u.description}),!N&&(0,l.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:n.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let s,a=(s=e.name,v.has(s));return(0,l.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${a?"":"opacity-60"}`,onClick:()=>j(e.name),children:[(0,l.jsx)(t.Checkbox,{checked:a,onChange:()=>j(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsx)(r.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,l.jsx)(r.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,l.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${a?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:a?"on":"off"})]},e.name)})})]},e)})})}],531516)},309426,e=>{"use strict";var l=e.i(290571),s=e.i(444755),t=e.i(673706),r=e.i(271645),a=e.i(46757);let i=(0,t.makeClassName)("Col"),n=r.default.forwardRef((e,t)=>{let n,o,c,d,{numColSpan:m=1,numColSpanSm:u,numColSpanMd:g,numColSpanLg:p,children:h,className:x}=e,f=(0,l.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),y=(e,l)=>e&&Object.keys(l).includes(String(e))?l[e]:"";return r.default.createElement("div",Object.assign({ref:t,className:(0,s.tremorTwMerge)(i("root"),(n=y(m,a.colSpan),o=y(u,a.colSpanSm),c=y(g,a.colSpanMd),d=y(p,a.colSpanLg),(0,s.tremorTwMerge)(n,o,c,d)),x)},f),h)});n.displayName="Col",e.s(["Col",0,n],309426)},355619,e=>{"use strict";var l=e.i(602869);let s=async(e,s,t)=>{try{if(null===e||null===s)return;if(null!==t){let r=(await (0,l.modelAvailableCall)(t,e,s,!0,null,!0)).data.map(e=>e.id),a=[],i=[];return r.forEach(e=>{e.endsWith("/*")?a.push(e):i.push(e)}),[...a,...i]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["fetchAvailableModelsForTeamOrKey",0,s,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let l=e.replace("/*","");return`All ${l} models`}return e},"unfurlWildcardModelsInList",0,(e,l)=>{let s=[],t=[];return e.forEach(e=>{if(e.endsWith("/*")){let r=e.replace("/*",""),a=l.filter(e=>e.startsWith(r+"/"));t.push(...a),s.push(e)}else t.push(e)}),[...s,...t].filter((e,l,s)=>s.indexOf(e)===l)}])},860585,e=>{"use strict";var l=e.i(843476),s=e.i(199133);let{Option:t}=s.Select;e.s(["default",0,({value:e,onChange:r,className:a="",style:i={}})=>(0,l.jsxs)(s.Select,{style:{width:"100%",...i},value:e||void 0,onChange:r,className:a,placeholder:"n/a",allowClear:!0,children:[(0,l.jsx)(t,{value:"1h",children:"hourly"}),(0,l.jsx)(t,{value:"24h",children:"daily"}),(0,l.jsx)(t,{value:"7d",children:"weekly"}),(0,l.jsx)(t,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},213205,e=>{"use strict";e.i(247167);var l=e.i(931067),s=e.i(271645);let t={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var r=e.i(9583),a=s.forwardRef(function(e,a){return s.createElement(r.default,(0,l.default)({},e,{ref:a,icon:t}))});e.s(["UserAddOutlined",0,a],213205)},350967,46757,e=>{"use strict";var l=e.i(290571),s=e.i(444755),t=e.i(673706),r=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},i={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},n={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},o={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,o,"gridColsMd",0,n,"gridColsSm",0,i],46757);let c=(0,t.makeClassName)("Grid"),d=(e,l)=>e&&Object.keys(l).includes(String(e))?l[e]:"",m=r.default.forwardRef((e,t)=>{let{numItems:m=1,numItemsSm:u,numItemsMd:g,numItemsLg:p,children:h,className:x}=e,f=(0,l.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),y=d(m,a),b=d(u,i),v=d(g,n),j=d(p,o),w=(0,s.tremorTwMerge)(y,b,v,j);return r.default.createElement("div",Object.assign({ref:t,className:(0,s.tremorTwMerge)(c("root"),"grid",w,x)},f),h)});m.displayName="Grid",e.s(["Grid",0,m],350967)},981339,e=>{"use strict";var l=e.i(185793);e.s(["Skeleton",()=>l.default])},500727,e=>{"use strict";var l=e.i(266027),s=e.i(243652),t=e.i(602869),r=e.i(135214);let a=(0,s.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:s}=(0,r.default)();return(0,l.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,t.fetchMCPServers)(s,e),enabled:!!s})}])},699857,e=>{"use strict";var l=e.i(266027),s=e.i(243652),t=e.i(602869),r=e.i(135214);let a=(0,s.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,r.default)();return(0,l.useQuery)({queryKey:a.list(),queryFn:async()=>await (0,t.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var l=e.i(843476),s=e.i(271645),t=e.i(199133),r=e.i(602869);e.s(["default",0,({onChange:e,value:a,className:i,accessToken:n,placeholder:o="Select vector stores",disabled:c=!1})=>{let[d,m]=(0,s.useState)([]),[u,g]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,r.vectorStoreListCall)(n);e.data&&m(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{g(!1)}}})()},[n]),(0,l.jsx)("div",{children:(0,l.jsx)(t.Select,{mode:"multiple",placeholder:o,onChange:e,value:a,loading:u,className:i,allowClear:!0,options:d.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:c})})}])},75921,e=>{"use strict";var l=e.i(843476),s=e.i(266027),t=e.i(243652),r=e.i(602869),a=e.i(135214);let i=(0,t.createQueryKeys)("mcpAccessGroups");var n=e.i(500727),o=e.i(699857),c=e.i(199133),d=e.i(234713);let m="toolset:";e.s(["default",0,({onChange:e,value:t,className:u,accessToken:g,placeholder:p="Select MCP servers",disabled:h=!1,teamId:x,allowNoMcpServers:f=!1,allowAllProxyMcpServers:y=!1})=>{let{data:b=[],isLoading:v}=(0,n.useMCPServers)(x),{data:j=[],isLoading:w}=(()=>{let{accessToken:e}=(0,a.default)();return(0,s.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,r.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:N=[],isLoading:S}=(0,o.useMCPToolsets)(),k=new Set(j),_=[...j.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...b.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...N.map(e=>({label:e.toolset_name,value:`${m}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],C={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...t?.servers||[],...t?.accessGroups||[],...(t?.toolsets||[]).map(e=>`${m}${e}`)],L=f&&E.includes(d.NO_MCP_SERVERS_SENTINEL),R=E.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,l.jsx)("div",{children:(0,l.jsxs)(c.Select,{mode:"multiple",placeholder:p,onChange:l=>{if(y&&l.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[d.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(f&&l.includes(d.NO_MCP_SERVERS_SENTINEL))return void e({servers:[d.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let s=l.filter(e=>e.startsWith(m)).map(e=>e.slice(m.length)),t=l.filter(e=>!e.startsWith(m));e({servers:t.filter(e=>!k.has(e)),accessGroups:t.filter(e=>k.has(e)),toolsets:s})},value:E,loading:v||w||S,className:u,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:h,filterOption:(e,l)=>l?.value===d.NO_MCP_SERVERS_SENTINEL||l?.value===d.ALL_PROXY_MCP_SERVERS_SENTINEL||(_.find(e=>e.value===l?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(y||R)&&(0,l.jsx)(c.Select.Option,{value:d.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,l.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},d.ALL_PROXY_MCP_SERVERS_SENTINEL),f&&(0,l.jsx)(c.Select.Option,{value:d.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,l.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,l.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},d.NO_MCP_SERVERS_SENTINEL),_.map(e=>(0,l.jsx)(c.Select.Option,{value:e.value,label:e.label,disabled:L||R,children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,l.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:C[e.type],flexShrink:0}}),(0,l.jsx)("span",{style:{flex:1},children:e.label}),(0,l.jsx)("span",{style:{color:C[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02j-r.3jw.7le.js b/litellm/proxy/_experimental/out/_next/static/chunks/02j-r.3jw.7le.js deleted file mode 100644 index 126c5975733..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/02j-r.3jw.7le.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,9314,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(981339),a=e.i(645526),r=e.i(599724),i=e.i(263147);e.s(["default",0,({value:e,onChange:n,placeholder:o="Select access groups",disabled:d=!1,style:c,className:u,showLabel:m=!1,labelText:p="Access Group",allowClear:g=!0})=>{let{data:h,isLoading:x,isError:y}=(0,i.useAccessGroups)();if(x)return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(l.Skeleton.Input,{active:!0,block:!0,style:{height:32,...c}})]});let f=(h??[]).map(e=>({label:(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"font-medium",children:e.access_group_name})," ",(0,t.jsxs)("span",{className:"text-gray-400 text-xs",children:["(",e.access_group_id,")"]})]}),value:e.access_group_id,selectedLabel:e.access_group_name,searchText:`${e.access_group_name} ${e.access_group_id}`}));return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(r.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(a.TeamOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(s.Select,{mode:"multiple",value:e,placeholder:o,onChange:n,disabled:d,allowClear:g,showSearch:!0,style:{width:"100%",...c},className:`rounded-md ${u??""}`,notFoundContent:y?(0,t.jsx)("span",{className:"text-red-500",children:"Failed to load access groups"}):"No access groups found",filterOption:(e,t)=>(f.find(e=>e.value===t?.value)?.searchText??"").toLowerCase().includes(e.toLowerCase()),optionLabelProp:"selectedLabel",options:f.map(e=>({label:e.label,value:e.value,selectedLabel:e.selectedLabel}))})]})}])},552130,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select agents",disabled:d=!1})=>{let[c,u]=(0,s.useState)([]),[m,p]=(0,s.useState)([]),[g,h]=(0,s.useState)(!1);(0,s.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,a.getAgentsList)(n),t=e?.agents||[];u(t);let s=new Set;t.forEach(e=>{let t=e.agent_access_groups;t&&Array.isArray(t)&&t.forEach(e=>s.add(e))}),p(Array.from(s))}catch(e){console.error("Error fetching agents:",e)}finally{h(!1)}}})()},[n]);let x=[...m.map(e=>({label:e,value:`group:${e}`,isAccessGroup:!0,searchText:`${e} Access Group`})),...c.map(e=>({label:`${e.agent_name||e.agent_id}`,value:e.agent_id,isAccessGroup:!1,searchText:`${e.agent_name||e.agent_id} ${e.agent_id} Agent`}))],y=[...r?.agents||[],...(r?.accessGroups||[]).map(e=>`group:${e}`)];return(0,t.jsx)("div",{children:(0,t.jsx)(l.Select,{mode:"multiple",placeholder:o,onChange:t=>{e({agents:t.filter(e=>!e.startsWith("group:")),accessGroups:t.filter(e=>e.startsWith("group:")).map(e=>e.replace("group:",""))})},value:y,loading:g,className:i,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:d,filterOption:(e,t)=>(x.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:x.map(e=>(0,t.jsx)(l.Select.Option,{value:e.value,label:e.label,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:e.isAccessGroup?"#52c41a":"#722ed1",flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:e.isAccessGroup?"#52c41a":"#722ed1",fontSize:"12px",fontWeight:500,opacity:.8},children:e.isAccessGroup?"Access Group":"Agent"})]})},e.value))})})}])},844565,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select pass through routes",disabled:d=!1,teamId:c})=>{let[u,m]=(0,s.useState)([]),[p,g]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.getPassThroughEndpointsCall)(n,c);if(e.endpoints){let t=e.endpoints.flatMap(e=>{let t=e.path,s=e.methods;return s&&s.length>0?s.map(e=>({label:`${e} ${t}`,value:t})):[{label:t,value:t}]});m(t)}}catch(e){console.error("Error fetching pass through routes:",e)}finally{g(!1)}}})()},[n,c]),(0,t.jsx)(l.Select,{mode:"tags",placeholder:o,onChange:e,value:r,loading:p,className:i,allowClear:!0,options:u,optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})}])},810757,477386,e=>{"use strict";var t=e.i(271645);let s=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}))});e.s(["CogIcon",0,s],810757);let l=t.forwardRef(function(e,s){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:s},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"}))});e.s(["BanIcon",0,l],477386)},557662,e=>{"use strict";let t="/ui/assets/logos/",s=[{id:"arize",displayName:"Arize",logo:`${t}arize.png`,supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_id:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:`${t}braintrust.png`,supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:`${t}custom.svg`,supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"galileo",displayName:"Galileo",logo:`${t}galileo.ico`,supports_key_team_logging:!1,dynamic_params:{GALILEO_API_KEY:"password",GALILEO_PROJECT_ID:"text",GALILEO_LOG_STREAM_ID:"text",GALILEO_BASE_URL:"text",GALILEO_USERNAME:"text",GALILEO_PASSWORD:"password"},description:"Galileo AI Observability Integration"},{id:"datadog",displayName:"Datadog",logo:`${t}datadog.png`,supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:`${t}lago.svg`,supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:`${t}langsmith.png`,supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:`${t}openmeter.png`,supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:`${t}otel.png`,supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"},{id:"SQS",displayName:"SQS",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{sqs_queue_url:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"SQS Queue (AWS) Logging Integration"}],l=s.reduce((e,t)=>(e[t.displayName]=t,e),{}),a=s.reduce((e,t)=>(e[t.displayName]=t.id,e),{}),r=s.reduce((e,t)=>(e[t.id]=t.displayName,e),{});e.s(["callbackInfo",0,l,"callback_map",0,a,"mapDisplayToInternalNames",0,e=>e.map(e=>a[e]||e),"mapInternalToDisplayNames",0,e=>e.map(e=>r[e]||e),"reverse_callback_map",0,r])},390605,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(602869),a=e.i(599724),r=e.i(482725),i=e.i(91739),n=e.i(500727),o=e.i(531516),d=e.i(696609);e.s(["default",0,({accessToken:e,selectedServers:c,toolPermissions:u,onChange:m,disabled:p=!1})=>{let{data:g=[]}=(0,n.useMCPServers)(),[h,x]=(0,s.useState)({}),[y,f]=(0,s.useState)({}),[b,_]=(0,s.useState)({}),[j,v]=(0,s.useState)({}),w=(0,s.useRef)(u);(0,s.useEffect)(()=>{w.current=u},[u]);let N=(0,s.useMemo)(()=>0===c.length?[]:g.filter(e=>c.includes(e.server_id)),[g,c]),k=async(e,t)=>{f(t=>({...t,[e]:!0})),_(t=>({...t,[e]:""}));try{let s=await (0,l.listMCPTools)(t,e);if(s.error)_(t=>({...t,[e]:s.message||"Failed to fetch tools"})),x(t=>({...t,[e]:[]}));else{let t=s.tools||[];x(s=>({...s,[e]:t}));let l=w.current;if(!l[e]&&t.length>0){let s=t.filter(e=>"delete"!==(0,d.classifyToolOp)(e.name,e.description||"")).map(e=>e.name);m({...l,[e]:s})}}}catch(t){console.error(`Error fetching tools for server ${e}:`,t),_(t=>({...t,[e]:"Failed to fetch tools"})),x(t=>({...t,[e]:[]}))}finally{f(t=>({...t,[e]:!1}))}};(0,s.useEffect)(()=>{N.forEach(t=>{h[t.server_id]||y[t.server_id]||k(t.server_id,e)})},[N,e]);let S=(e,t)=>{m({...u,[e]:t})};return 0===c.length?null:(0,t.jsx)("div",{className:"space-y-4",children:N.map(e=>{let s=e.server_name||e.alias||e.server_id,l=h[e.server_id]||[],n=u[e.server_id]||[],d=y[e.server_id],c=b[e.server_id],g=j[e.server_id]??"crud";return(0,t.jsxs)("div",{className:"border rounded-lg bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-4 border-b bg-white rounded-t-lg",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-semibold text-gray-900",children:s}),e.description&&(0,t.jsx)(a.Text,{className:"text-sm text-gray-500",children:e.description})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[!p&&l.length>0&&(0,t.jsx)(i.Radio.Group,{value:g,onChange:t=>v(s=>({...s,[e.server_id]:t.target.value})),size:"small",optionType:"button",buttonStyle:"solid",options:[{label:"Risk Groups",value:"crud"},{label:"Flat List",value:"flat"}]}),!p&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;let s;return s=h[t=e.server_id]||[],void m({...u,[t]:s.map(e=>e.name)})},disabled:d,children:"Select All"}),(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;return t=e.server_id,void m({...u,[t]:[]})},disabled:d,children:"Deselect All"})]})]})]}),(0,t.jsxs)("div",{className:"p-4",children:[d&&(0,t.jsxs)("div",{className:"flex items-center justify-center py-8",children:[(0,t.jsx)(r.Spin,{size:"large"}),(0,t.jsx)(a.Text,{className:"ml-3 text-gray-500",children:"Loading tools..."})]}),c&&!d&&(0,t.jsxs)("div",{className:"p-4 bg-red-50 border border-red-200 rounded-lg text-center",children:[(0,t.jsx)(a.Text,{className:"text-red-600 font-medium",children:"Unable to load tools"}),(0,t.jsx)(a.Text,{className:"text-sm text-red-500 mt-1",children:c})]}),!d&&!c&&l.length>0&&"crud"===g&&(0,t.jsx)(o.default,{tools:l,value:u[e.server_id]?n:void 0,onChange:t=>S(e.server_id,t),readOnly:p}),!d&&!c&&l.length>0&&"flat"===g&&(0,t.jsx)("div",{className:"space-y-2",children:l.map(s=>{let l=n.includes(s.name);return(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("input",{type:"checkbox",checked:l,onChange:()=>{if(p)return;let t=l?n.filter(e=>e!==s.name):[...n,s.name];S(e.server_id,t)},disabled:p,className:"mt-0.5"}),(0,t.jsx)("div",{className:"flex-1 min-w-0",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900",children:s.name}),(0,t.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["- ",s.description||"No description"]})]})})]},s.name)})}),!d&&!c&&0===l.length&&(0,t.jsx)("div",{className:"text-center py-6",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"No tools available"})})]})]},e.server_id)})})}])},266484,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(592968),a=e.i(312361),r=e.i(827252),i=e.i(994388),n=e.i(304967),o=e.i(779241),d=e.i(988297),c=e.i(68155),u=e.i(810757),m=e.i(477386),p=e.i(557662),g=e.i(555987),h=e.i(435451);let{Option:x}=s.Select;e.s(["default",0,({value:e=[],onChange:y,disabledCallbacks:f=[],onDisabledCallbacksChange:b})=>{let _=Object.entries(p.callbackInfo).filter(([e,t])=>t.supports_key_team_logging).map(([e,t])=>e),j=Object.keys(p.callbackInfo),v=e=>{y?.(e)},w=(t,s,l)=>{let a=[...e];if("callback_name"===s){let e=p.callback_map[l]||l;a[t]={...a[t],[s]:e,callback_vars:{}}}else a[t]={...a[t],[s]:l};v(a)},N=(t,s,l)=>{let a=[...e];a[t]={...a[t],callback_vars:{...a[t].callback_vars,[s]:l}},v(a)};return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(m.BanIcon,{className:"w-5 h-5 text-red-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Disabled Callbacks"}),(0,t.jsx)(l.Tooltip,{title:"Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Disabled Callbacks"}),(0,t.jsx)(s.Select,{mode:"multiple",placeholder:"Select callbacks to disable",value:f,onChange:e=>{let t=(0,p.mapDisplayToInternalNames)(e);b?.(t)},style:{width:"100%"},optionLabelProp:"label",children:j.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Select callbacks that should be disabled for this key. These callbacks will not receive any logging data."})]})]}),(0,t.jsx)(a.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(u.CogIcon,{className:"w-5 h-5 text-blue-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Logging Integrations"}),(0,t.jsx)(l.Tooltip,{title:"Configure callback logging integrations for this team.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsx)(i.Button,{variant:"secondary",onClick:()=>{v([...e,{callback_name:"",callback_type:"success",callback_vars:{}}])},icon:d.PlusIcon,size:"sm",className:"hover:border-blue-400 hover:text-blue-500",type:"button",children:"Add Integration"})]}),(0,t.jsx)("div",{className:"space-y-4",children:e.map((a,d)=>{let u=a.callback_name?Object.entries(p.callback_map).find(([e,t])=>t===a.callback_name)?.[0]:void 0,m=u?(0,g.resolveLogoSrc)(p.callbackInfo[u]?.logo):null;return(0,t.jsxs)(n.Card,{className:"border border-gray-200 shadow-xs hover:shadow-md transition-shadow duration-200",decoration:"top",decorationColor:"blue",children:[(0,t.jsxs)("div",{className:"flex justify-between items-start mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[m&&(0,t.jsx)("img",{src:m,alt:u,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("span",{className:"text-sm font-medium",children:[u||"New Integration"," Configuration"]})]}),(0,t.jsx)(i.Button,{variant:"light",onClick:()=>{v(e.filter((e,t)=>t!==d))},icon:c.TrashIcon,size:"xs",color:"red",className:"hover:bg-red-50",type:"button",children:"Remove"})]}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Integration Type"}),(0,t.jsx)(s.Select,{value:u,placeholder:"Select integration",onChange:e=>w(d,"callback_name",e),className:"w-full",optionLabelProp:"label",children:_.map(e=>{let s=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(l.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,t.jsx)("img",{src:s,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let s=t.target,l=s.parentElement;if(l){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),l.replaceChild(t,s)}}}),(0,t.jsx)("span",{children:e})]})})},e)})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Event Type"}),(0,t.jsxs)(s.Select,{value:a.callback_type,onChange:e=>w(d,"callback_type",e),className:"w-full",children:[(0,t.jsx)(x,{value:"success",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),(0,t.jsx)("span",{children:"Success Only"})]})}),(0,t.jsx)(x,{value:"failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-red-500 rounded-full"}),(0,t.jsx)("span",{children:"Failure Only"})]})}),(0,t.jsx)(x,{value:"success_and_failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),(0,t.jsx)("span",{children:"Success & Failure"})]})})]})]})]}),((e,s)=>{if(!e.callback_name)return null;let a=Object.entries(p.callback_map).find(([t,s])=>s===e.callback_name)?.[0];if(!a)return null;let i=p.callbackInfo[a]?.dynamic_params||{};return 0===Object.keys(i).length?null:(0,t.jsxs)("div",{className:"mt-6 pt-4 border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2 mb-4",children:[(0,t.jsx)("div",{className:"w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center",children:(0,t.jsx)("div",{className:"w-1.5 h-1.5 bg-blue-500 rounded-full"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Integration Parameters"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-4",children:Object.entries(i).map(([a,i])=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 capitalize flex items-center space-x-1",children:[(0,t.jsx)("span",{children:a.replace(/_/g," ")}),(0,t.jsx)(l.Tooltip,{title:`Environment variable reference recommended: os.environ/${a.toUpperCase()}`,children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),"password"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Sensitive"}),"number"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Number"})]}),"number"===i&&(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Value must be between 0 and 1"}),"number"===i?(0,t.jsx)(h.default,{step:.01,width:400,placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)}):(0,t.jsx)(o.TextInput,{type:"password"===i?"password":"text",placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>N(s,a,e.target.value)})]},a))})]})})(a,d)]})]},d)})}),0===e.length&&(0,t.jsxs)("div",{className:"text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50",children:[(0,t.jsx)(u.CogIcon,{className:"w-12 h-12 text-gray-300 mb-3 mx-auto"}),(0,t.jsx)("div",{className:"text-base font-medium mb-1",children:"No logging integrations configured"}),(0,t.jsx)("div",{className:"text-sm text-gray-400",children:'Click "Add Integration" to configure logging for this team'})]})]})}])},460285,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(404206),a=e.i(723731),r=e.i(653824),i=e.i(881073),n=e.i(197647),o=e.i(602869),d=e.i(158392),c=e.i(419470),u=e.i(695411);let m=(0,s.forwardRef)(({accessToken:e,value:m,onChange:p,modelData:g},h)=>{let[x,y]=(0,s.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[f,b]=(0,s.useState)([]),[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)([]),[N,k]=(0,s.useState)([]),[S,C]=(0,s.useState)({}),[T,I]=(0,s.useState)({}),A=(0,s.useRef)(!1),L=(0,s.useRef)(null);(0,s.useEffect)(()=>{let e=m?.router_settings?JSON.stringify({routing_strategy:m.router_settings.routing_strategy,fallbacks:m.router_settings.fallbacks,enable_tag_filtering:m.router_settings.enable_tag_filtering}):null;if(A.current&&e===L.current){A.current=!1;return}if(A.current&&e!==L.current&&(A.current=!1),e!==L.current)if(L.current=e,m?.router_settings){let e=m.router_settings,{fallbacks:t,...s}=e;y({routerSettings:s,selectedStrategy:e.routing_strategy||null,enableTagFiltering:e.enable_tag_filtering??!1});let l=e.fallbacks||[];b(l),j(l&&0!==l.length?l.map((e,t)=>{let[s,l]=Object.entries(e)[0];return{id:(t+1).toString(),primaryModel:s||null,fallbackModels:l||[]}}):[{id:"1",primaryModel:null,fallbackModels:[]}])}else y({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),b([]),j([{id:"1",primaryModel:null,fallbackModels:[]}])},[m]),(0,s.useEffect)(()=>{e&&(0,o.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),C(t);let s=e.fields.find(e=>"routing_strategy"===e.field_name);s?.options&&k(s.options),e.routing_strategy_descriptions&&I(e.routing_strategy_descriptions)}})},[e]),(0,s.useEffect)(()=>{e&&(async()=>{try{let t=await (0,u.fetchAvailableModels)(e);w(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}})()},[e]);let F=()=>{let e=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),t=new Set(["model_group_alias","retry_policy"]),s=Object.fromEntries(Object.entries({...x.routerSettings,enable_tag_filtering:x.enableTagFiltering,routing_strategy:x.selectedStrategy,fallbacks:f.length>0?f:null}).map(([s,l])=>{if("routing_strategy_args"!==s&&"routing_strategy"!==s&&"enable_tag_filtering"!==s&&"fallbacks"!==s){let a=document.querySelector(`input[name="${s}"]`);if(a){if(void 0!==a.value&&""!==a.value){let r=((s,l,a)=>{if(null==l)return a;let r=String(l).trim();if(""===r||"null"===r.toLowerCase())return null;if(e.has(s)){let e=Number(r);return Number.isNaN(e)?a:e}if(t.has(s)){if(""===r)return null;try{return JSON.parse(r)}catch{return a}}return"true"===r.toLowerCase()||"false"!==r.toLowerCase()&&r})(s,a.value,l);return[s,r]}return[s,null]}}else if("routing_strategy"===s)return[s,x.selectedStrategy];else if("enable_tag_filtering"===s)return[s,x.enableTagFiltering];else if("fallbacks"===s)return[s,f.length>0?f:null];else if("routing_strategy_args"===s&&"latency-based-routing"===x.selectedStrategy){let e=document.querySelector('input[name="lowest_latency_buffer"]'),t=document.querySelector('input[name="ttl"]'),s={};return e?.value&&(s.lowest_latency_buffer=Number(e.value)),t?.value&&(s.ttl=Number(t.value)),["routing_strategy_args",Object.keys(s).length>0?s:null]}return[s,l]}).filter(e=>null!=e)),l=(e,t=!1)=>null==e||"object"==typeof e&&!Array.isArray(e)&&0===Object.keys(e).length||t&&("number"!=typeof e||Number.isNaN(e))?null:e;return{routing_strategy:l(s.routing_strategy),allowed_fails:l(s.allowed_fails,!0),cooldown_time:l(s.cooldown_time,!0),num_retries:l(s.num_retries,!0),timeout:l(s.timeout,!0),retry_after:l(s.retry_after,!0),fallbacks:f.length>0?f:null,context_window_fallbacks:l(s.context_window_fallbacks),retry_policy:l(s.retry_policy),model_group_alias:l(s.model_group_alias),enable_tag_filtering:x.enableTagFiltering,routing_strategy_args:l(s.routing_strategy_args)}};(0,s.useEffect)(()=>{if(!p)return;let e=setTimeout(()=>{A.current=!0,p({router_settings:F()})},100);return()=>clearTimeout(e)},[x,f]);let M=Array.from(new Set(v.map(e=>e.model_group))).sort();return((0,s.useImperativeHandle)(h,()=>({getValue:()=>({router_settings:F()})})),e)?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(r.TabGroup,{className:"w-full",children:[(0,t.jsxs)(i.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(n.Tab,{value:"2",children:"Fallbacks"})]}),(0,t.jsxs)(a.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(d.default,{value:x,onChange:y,routerFieldsMetadata:S,availableRoutingStrategies:N,routingStrategyDescriptions:T})}),(0,t.jsx)(l.TabPanel,{children:(0,t.jsx)(c.FallbackSelectionForm,{groups:_,onGroupsChange:e=>{j(e),b(e.filter(e=>e.primaryModel&&e.fallbackModels.length>0).map(e=>({[e.primaryModel]:e.fallbackModels})))},availableModels:M,maxGroups:5})})]})]})}):null});m.displayName="RouterSettingsAccordion",e.s(["default",0,m])},207082,e=>{"use strict";var t=e.i(619273),s=e.i(266027),l=e.i(243652),a=e.i(602869),r=e.i(431703),i=e.i(135214);let n=(0,l.createQueryKeys)("keys"),o=async(e,t,s,l={})=>{try{let i=(0,a.getProxyBaseUrl)(),n=new URLSearchParams(Object.entries({team_id:l.teamID,project_id:l.projectID,agent_id:l.agentID,organization_id:l.organizationID,key_alias:l.selectedKeyAlias,key_hash:l.keyHash,user_id:l.userID,page:t,size:s,sort_by:l.sortBy,sort_order:l.sortOrder,expand:l.expand,status:l.status,return_full_object:"true",include_team_keys:"true",include_created_by_keys:"true",substring_matching:"true"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),o=`${i?`${i}/key/list`:"/key/list"}?${n}`,d=await fetch(o,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!d.ok){let e=await d.json(),t=(0,r.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return await d.json()}catch(e){throw console.error("Failed to list keys:",e),e}},d=(0,l.createQueryKeys)("deletedKeys");e.s(["keyKeys",0,n,"useDeletedKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:d.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,{...a,status:"deleted"}),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})},"useKeys",0,(e,l,a={})=>{let{accessToken:r}=(0,i.default)();return(0,s.useQuery)({queryKey:n.list({page:e,limit:l,...a}),queryFn:async()=>await o(r,e,l,a),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})}])},510674,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(431703),r=e.i(135214),i=e.i(708347);let n=(0,s.createQueryKeys)("projects"),o=[...i.all_admin_roles,...i.internalUserRoles],d=async e=>{let t=(0,l.getProxyBaseUrl)(),s=`${t}/project/list`,r=await fetch(s,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,a.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return r.json()};e.s(["projectKeys",0,n,"useProjects",0,()=>{let{accessToken:e,userRole:s}=(0,r.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>d(e),enabled:!!e&&o.includes(s)})}])},392110,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(199133),a=e.i(592968),r=e.i(312361),i=e.i(790848),n=e.i(536916),o=e.i(827252),d=e.i(779241);let{Option:c}=l.Select;e.s(["default",0,({form:e,autoRotationEnabled:u,onAutoRotationChange:m,rotationInterval:p,onRotationIntervalChange:g,isCreateMode:h=!1,neverExpire:x=!1,onNeverExpireChange:y})=>{let f=p&&!["7d","30d","90d","180d","365d"].includes(p),[b,_]=(0,s.useState)(f),[j,v]=(0,s.useState)(f?p:""),[w,N]=(0,s.useState)(e?.getFieldValue?.("duration")||"");return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Key Expiry Settings"}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Expire Key"}),(0,t.jsx)(a.Tooltip,{title:"Set when this key should expire. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days). Leave empty to keep the current expiry unchanged.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),!h&&y&&(0,t.jsx)(n.Checkbox,{checked:x,onChange:t=>{let s=t.target.checked;y(s),s&&(N(""),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",""):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:""}))},className:"ml-2 text-sm font-normal text-gray-600",children:"Never Expire"})]}),(0,t.jsx)(d.TextInput,{name:"duration",placeholder:h?"e.g., 30d or leave empty to never expire":"e.g., 30d",className:"w-full",value:w,onValueChange:t=>{N(t),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",t):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:t})},disabled:!h&&x})]})]}),(0,t.jsx)(r.Divider,{}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Auto-Rotation Settings"}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Enable Auto-Rotation"}),(0,t.jsx)(a.Tooltip,{title:"Key will automatically regenerate at the specified interval for enhanced security.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsx)(i.Switch,{checked:u,onChange:m,size:"default",className:u?"":"bg-gray-400"})]}),u&&(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Rotation Interval"}),(0,t.jsx)(a.Tooltip,{title:"How often the key should be automatically rotated. Choose the interval that best fits your security requirements.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)(l.Select,{value:b?"custom":p,onChange:e=>{"custom"===e?_(!0):(_(!1),v(""),g(e))},className:"w-full",placeholder:"Select interval",children:[(0,t.jsx)(c,{value:"7d",children:"7 days"}),(0,t.jsx)(c,{value:"30d",children:"30 days"}),(0,t.jsx)(c,{value:"90d",children:"90 days"}),(0,t.jsx)(c,{value:"180d",children:"180 days"}),(0,t.jsx)(c,{value:"365d",children:"365 days"}),(0,t.jsx)(c,{value:"custom",children:"Custom interval"})]}),b&&(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(d.TextInput,{value:j,onChange:e=>{let t=e.target.value;v(t),g(t)},placeholder:"e.g., 1s, 5m, 2h, 14d"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Supported formats: seconds (s), minutes (m), hours (h), days (d)"})]})]})]})]}),u&&(0,t.jsx)("div",{className:"bg-blue-50 p-3 rounded-md text-sm text-blue-700",children:"When rotation occurs, you'll receive a notification with the new key. The old key will be deactivated after a brief grace period."})]})]})}])},939510,e=>{"use strict";var t=e.i(843476),s=e.i(808613),l=e.i(199133),a=e.i(592968),r=e.i(827252);let{Option:i}=l.Select;e.s(["default",0,({type:e,name:n,showDetailedDescriptions:o=!0,className:d="",initialValue:c=null,form:u,onChange:m})=>{let p=e.toUpperCase(),g=e.toLowerCase(),h=`Select 'guaranteed_throughput' to prevent overallocating ${p} limit when the key belongs to a Team with specific ${p} limits.`;return(0,t.jsx)(s.Form.Item,{label:(0,t.jsxs)("span",{children:[p," Rate Limit Type"," ",(0,t.jsx)(a.Tooltip,{title:h,children:(0,t.jsx)(r.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:n,initialValue:c,className:d,children:(0,t.jsx)(l.Select,{defaultValue:o?"default":void 0,placeholder:"Select rate limit type",style:{width:"100%"},optionLabelProp:o?"label":void 0,onChange:e=>{u&&u.setFieldValue(n,e),m&&m(e)},children:o?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",label:"Default",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Default"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Best effort throughput - no error if we're overallocating ",g," (Team/Key Limits checked at runtime)."]})]})}),(0,t.jsx)(i,{value:"guaranteed_throughput",label:"Guaranteed throughput",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Guaranteed throughput"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Guaranteed throughput - raise an error if we're overallocating ",g," (also checks model-specific limits)"]})]})}),(0,t.jsx)(i,{value:"dynamic",label:"Dynamic",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Dynamic"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["If the key has a set ",p," (e.g. 2 ",p,") and there are no 429 errors, it can dynamically exceed the limit when the model being called is not erroring."]})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",children:"Best effort throughput"}),(0,t.jsx)(i,{value:"guaranteed_throughput",children:"Guaranteed throughput"}),(0,t.jsx)(i,{value:"dynamic",children:"Dynamic"})]})})})}])},363256,e=>{"use strict";var t=e.i(843476),s=e.i(199133);let{Text:l}=e.i(898586).Typography;e.s(["default",0,({organizations:e,value:a,onChange:r,disabled:i,loading:n,style:o})=>(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"All Organizations",value:a,onChange:r,disabled:i,loading:n,allowClear:!0,style:{minWidth:280,...o},filterOption:(t,s)=>{if(!s)return!1;let l=e?.find(e=>e.organization_id===s.key);if(!l)return!1;let a=t.toLowerCase().trim(),r=(l.organization_alias||"").toLowerCase(),i=(l.organization_id||"").toLowerCase();return r.includes(a)||i.includes(a)},children:e?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.organization_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.organization_alias})," ",(0,t.jsxs)(l,{type:"secondary",children:["(",e.organization_id,")"]})]},e.organization_id))})])},128233,319312,e=>{"use strict";var t=e.i(843476),s=e.i(464571),l=e.i(199133),a=e.i(592968),r=e.i(425063),i=e.i(107233),n=e.i(37727),o=e.i(271645);e.s(["BudgetFallbacksEditor",0,function({value:e,onChange:d,availableModels:c}){let[u,m]=(0,o.useState)(()=>{let t;return 0===(t=Object.keys(e)).length?[]:t.map((t,s)=>({id:String(s+1),primaryModel:t,fallbackModels:e[t]}))}),p=e=>{m(e),d(Object.fromEntries(e.filter(e=>null!==e.primaryModel&&e.fallbackModels.length>0).map(e=>[e.primaryModel,e.fallbackModels])))},g=()=>{p([...u,{id:Date.now().toString(),primaryModel:null,fallbackModels:[]}])},h=(e,t)=>{p(u.map(s=>s.id===e?{...s,...t}:s))},x=new Set(u.map(e=>e.primaryModel).filter(Boolean));return 0===u.length?(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-2",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),u.map(e=>{let s=c.filter(t=>t===e.primaryModel||!x.has(t)),i=c.filter(t=>t!==e.primaryModel);return(0,t.jsxs)("div",{className:"relative rounded-lg border border-gray-200 bg-gray-50 p-4",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{var t;return t=e.id,void p(u.filter(e=>e.id!==t))},className:"absolute top-2 right-2 text-gray-400 hover:text-red-500 transition-colors p-1",children:(0,t.jsx)(n.X,{className:"w-4 h-4"})}),(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Primary Model"}),(0,t.jsx)(l.Select,{className:"w-full",placeholder:"Select model",value:e.primaryModel,onChange:t=>{let s=e.fallbackModels.filter(e=>e!==t);h(e.id,{primaryModel:t,fallbackModels:s})},showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:s.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-1 mb-2",children:(0,t.jsxs)("div",{className:"bg-amber-50 text-amber-600 px-3 py-0.5 rounded-full text-[10px] font-bold border border-amber-100 flex items-center gap-1",children:[(0,t.jsx)(r.ArrowDown,{className:"w-3 h-3"}),"IF BUDGET EXCEEDED, TRY"]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Fallback Models"}),(0,t.jsx)(l.Select,{mode:"multiple",className:"w-full",placeholder:e.primaryModel?"Select fallback models":"Select a primary model first",value:e.fallbackModels,onChange:t=>h(e.id,{fallbackModels:t}),disabled:!e.primaryModel,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:i.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(a.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})}),e.fallbackModels.length>1&&(0,t.jsx)("div",{className:"text-[10px] text-gray-400 mt-1 ml-1",children:"Tried in order; first model still within its own budget is used"})]})]},e.id)}),(0,t.jsx)(s.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]})}],128233);var d=e.i(28651);let c=[{value:"1h",label:"Hourly",resetHint:"Resets every hour"},{value:"24h",label:"Daily",resetHint:"Resets daily at midnight UTC"},{value:"7d",label:"Weekly",resetHint:"Resets every Sunday at midnight UTC"},{value:"30d",label:"Monthly",resetHint:"Resets on the 1st of every month at midnight UTC"}];e.s(["BudgetWindowsEditor",0,function({value:e,onChange:a}){let r=(t,s,l)=>{a(e.map((e,a)=>a===t?{...e,[s]:l}:e))};return(0,t.jsxs)("div",{children:[e.map((i,n)=>{let o=c.find(e=>e.value===i.budget_duration)?.resetHint;return(0,t.jsxs)("div",{style:{marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsx)(l.Select,{value:i.budget_duration,onChange:e=>r(n,"budget_duration",e),style:{width:130},options:c.map(e=>({value:e.value,label:e.label}))}),(0,t.jsx)(d.InputNumber,{step:.01,min:0,precision:2,value:i.max_budget??void 0,onChange:e=>r(n,"max_budget",e??null),placeholder:"Max spend ($)",style:{width:160},prefix:"$"}),(0,t.jsx)(s.Button,{type:"text",danger:!0,size:"small",onClick:()=>{a(e.filter((e,t)=>t!==n))},style:{padding:"0 4px"},children:"✕"})]}),o&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#888",marginTop:3,marginLeft:2},children:["↻ ",o]})]},n)}),(0,t.jsx)(s.Button,{size:"small",onClick:t=>{t.preventDefault(),a([...e,{budget_duration:"24h",max_budget:null}])},children:"+ Add Budget Window"})]})}],319312)},109034,e=>{"use strict";var t=e.i(266027),s=e.i(243652),l=e.i(602869),a=e.i(135214);let r=(0,s.createQueryKeys)("tags");e.s(["useTags",0,()=>{let{accessToken:e,userId:s,userRole:i}=(0,a.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,l.tagListCall)(e),enabled:!!(e&&s&&i)})}])},533882,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(250980),a=e.i(797672),r=e.i(68155),i=e.i(304967),n=e.i(629569),o=e.i(599724),d=e.i(269200),c=e.i(427612),u=e.i(64848),m=e.i(942232),p=e.i(496020),g=e.i(977572),h=e.i(992619),x=e.i(727749);e.s(["default",0,({accessToken:e,initialModelAliases:y={},onAliasUpdate:f,showExampleConfig:b=!0})=>{let[_,j]=(0,s.useState)([]),[v,w]=(0,s.useState)({aliasName:"",targetModel:""}),[N,k]=(0,s.useState)(null);(0,s.useEffect)(()=>{j(Object.entries(y).map(([e,t],s)=>({id:`${s}-${e}`,aliasName:e,targetModel:t})))},[y]);let S=()=>{if(!N)return;if(!N.aliasName||!N.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.id!==N.id&&e.aliasName===N.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=_.map(e=>e.id===N.id?N:e);j(e),k(null);let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias updated successfully")},C=()=>{k(null)},T=_.reduce((e,t)=>(e[t.aliasName]=t.targetModel,e),{});return(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:v.aliasName,onChange:e=>w({...v,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model"}),(0,t.jsx)(h.default,{accessToken:e,value:v.targetModel,placeholder:"Select target model",onChange:e=>w({...v,targetModel:e}),showLabel:!1})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:()=>{if(!v.aliasName||!v.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.aliasName===v.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=[..._,{id:`${Date.now()}-${v.aliasName}`,aliasName:v.aliasName,targetModel:v.targetModel}];j(e),w({aliasName:"",targetModel:""});let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias added successfully")},disabled:!v.aliasName||!v.targetModel,className:`flex items-center px-4 py-2 rounded-md text-sm ${!v.aliasName||!v.targetModel?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(l.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(d.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(c.TableHead,{children:(0,t.jsxs)(p.TableRow,{children:[(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Target Model"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(m.TableBody,{children:[_.map(s=>(0,t.jsx)(p.TableRow,{className:"h-8",children:N&&N.id===s.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:N.aliasName,onChange:e=>k({...N,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)(h.default,{accessToken:e,value:N.targetModel,onChange:e=>k({...N,targetModel:e}),showLabel:!1,style:{height:"32px"}})}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:S,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:C,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-900",children:s.aliasName}),(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-500",children:s.targetModel}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{k({...s})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(a.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>{var e;let t,l;return e=s.id,j(t=_.filter(t=>t.id!==e)),l={},void(t.forEach(e=>{l[e.aliasName]=e.targetModel}),f&&f(l),x.default.success("Alias deleted successfully"))},className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(r.TrashIcon,{className:"w-3 h-3"})})]})})]})},s.id)),0===_.length&&(0,t.jsx)(p.TableRow,{children:(0,t.jsx)(g.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),b&&(0,t.jsxs)(i.Card,{children:[(0,t.jsx)(n.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(o.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["model_aliases:",0===Object.keys(T).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"  # No aliases configured yet"]}):Object.entries(T).map(([e,s])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'  "',e,'": "',s,'"']},e))]})})]})]})}])},651904,e=>{"use strict";var t=e.i(843476),s=e.i(599724),l=e.i(266484);e.s(["default",0,function({value:e,onChange:a,premiumUser:r=!1,disabledCallbacks:i=[],onDisabledCallbacksChange:n}){return r?(0,t.jsx)(l.default,{value:e,onChange:a,disabledCallbacks:i,onDisabledCallbacksChange:n}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex flex-wrap gap-2 mb-3",children:[(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ langfuse-logging"}),(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ datadog-logging"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(s.Text,{className:"text-sm text-yellow-800",children:["Setting Key/Team logging settings is a LiteLLM Enterprise feature. Global Logging Settings are available for all free users. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})}])},575260,e=>{"use strict";var t=e.i(843476),s=e.i(199133),l=e.i(482725),a=e.i(56456);e.s(["default",0,({projects:e,value:r,onChange:i,disabled:n,loading:o,teamId:d})=>{let c=d?e?.filter(e=>e.team_id===d):e;return(0,t.jsx)(s.Select,{showSearch:!0,placeholder:"Search or select a project",value:r,onChange:i,disabled:n,loading:o,allowClear:!0,notFoundContent:o?(0,t.jsx)(l.Spin,{indicator:(0,t.jsx)(a.LoadingOutlined,{spin:!0}),size:"small"}):void 0,filterOption:(e,t)=>{if(!t)return!1;let s=c?.find(e=>e.project_id===t.key);if(!s)return!1;let l=e.toLowerCase().trim(),a=(s.project_alias||"").toLowerCase(),r=(s.project_id||"").toLowerCase();return a.includes(l)||r.includes(l)},optionFilterProp:"children",children:!o&&c?.map(e=>(0,t.jsxs)(s.Select.Option,{value:e.project_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.project_alias||e.project_id})," ",(0,t.jsxs)("span",{className:"text-gray-500",children:["(",e.project_id,")"]})]},e.project_id))})}])},364769,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(237016),a=e.i(464571),r=e.i(888259);e.s(["default",0,({apiKey:e})=>{let[i,n]=(0,s.useState)(!1);return(0,t.jsxs)("div",{children:[(0,t.jsxs)("p",{className:"mb-2",children:["Please save this secret key somewhere safe and accessible. For security reasons,"," ",(0,t.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]}),(0,t.jsx)("p",{className:"text-sm text-gray-600 mt-3 mb-1",children:"Virtual Key:"}),(0,t.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,t.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal",margin:0},children:e})}),(0,t.jsx)(l.CopyToClipboard,{text:e,onCopy:()=>{n(!0),r.default.success("Key copied to clipboard"),setTimeout(()=>n(!1),2e3)},children:(0,t.jsx)(a.Button,{type:"primary",style:{marginTop:12},children:i?"Copied!":"Copy Virtual Key"})})]})}])},702597,e=>{"use strict";var t=e.i(843476),s=e.i(207082),l=e.i(109799),a=e.i(510674),r=e.i(109034),i=e.i(292639),n=e.i(135214),o=e.i(500330),d=e.i(827252),c=e.i(912598),u=e.i(677667),m=e.i(130643),p=e.i(898667),g=e.i(994388),h=e.i(309426),x=e.i(350967),y=e.i(599724),f=e.i(779241),b=e.i(629569),_=e.i(464571),j=e.i(808613),v=e.i(311451),w=e.i(212931),N=e.i(91739),k=e.i(199133),S=e.i(790848),C=e.i(262218),T=e.i(592968),I=e.i(898586),A=e.i(374009),L=e.i(271645),F=e.i(708347),M=e.i(552130),O=e.i(557662),E=e.i(9314),P=e.i(860585),B=e.i(82946),$=e.i(392110),R=e.i(533882),D=e.i(844565),V=e.i(651904),z=e.i(939510),U=e.i(460285),G=e.i(663435),K=e.i(363256),q=e.i(575260),W=e.i(371455),H=e.i(128233),Q=e.i(319312),J=e.i(355619),Y=e.i(75921),X=e.i(234713),Z=e.i(390605),ee=e.i(727749),et=e.i(602869),es=e.i(364769),el=e.i(435451),ea=e.i(916940);let{Option:er}=k.Select,ei=async(e,t,s,l)=>{try{if(null===e||null===t)return[];if(null!==s)return(await (0,et.modelAvailableCall)(s,e,t,!0,l,!0)).data.map(e=>e.id);return[]}catch(e){return console.error("Error fetching user models:",e),[]}},en=async(e,t,s,l)=>{try{if(null===e||null===t)return;if(null!==s){let a=(await (0,et.modelAvailableCall)(s,e,t)).data.map(e=>e.id);l(a)}}catch(e){console.error("Error fetching user models:",e)}};e.s(["default",0,({team:e,teams:eo,data:ed,addKey:ec,autoOpenCreate:eu,prefillData:em})=>{let{accessToken:ep,userId:eg,userRole:eh,premiumUser:ex}=(0,n.default)(),ey=ex||null!=eh&&F.rolesWithWriteAccess.includes(eh),{data:ef,isLoading:eb}=(0,l.useOrganizations)(),{data:e_,isLoading:ej}=(0,a.useProjects)(),{data:ev}=(0,i.useUISettings)(),{data:ew}=(0,r.useTags)(),eN=!!ev?.values?.enable_projects_ui,ek=!!ev?.values?.disable_custom_api_keys,eS=ew?Object.values(ew).map(e=>({value:e.name,label:e.name})):[],eC=(0,c.useQueryClient)(),[eT]=j.Form.useForm(),[eI,eA]=(0,L.useState)(!1),[eL,eF]=(0,L.useState)(null),[eM,eO]=(0,L.useState)(null),[eE,eP]=(0,L.useState)([]),[eB,e$]=(0,L.useState)([]),[eR,eD]=(0,L.useState)("you"),[eV,ez]=(0,L.useState)(!1),[eU,eG]=(0,L.useState)(null),[eK,eq]=(0,L.useState)([]),[eW,eH]=(0,L.useState)([]),[eQ,eJ]=(0,L.useState)([]),[eY,eX]=(0,L.useState)([]),[eZ,e0]=(0,L.useState)(e),[e1,e2]=(0,L.useState)(null),[e4,e3]=(0,L.useState)(null),[e5,e6]=(0,L.useState)(!1),[e7,e9]=(0,L.useState)(null),[e8,te]=(0,L.useState)({}),[tt,ts]=(0,L.useState)([]),[tl,ta]=(0,L.useState)(!1),[tr,ti]=(0,L.useState)([]),[tn,to]=(0,L.useState)([]),[td,tc]=(0,L.useState)("llm_api"),[tu,tm]=(0,L.useState)({}),[tp,tg]=(0,L.useState)(!1),[th,tx]=(0,L.useState)("30d"),[ty,tf]=(0,L.useState)(null),[tb,t_]=(0,L.useState)([]),[tj,tv]=(0,L.useState)({}),[tw,tN]=(0,L.useState)(0),[tk,tS]=(0,L.useState)(0),[tC,tT]=(0,L.useState)([]),[tI,tA]=(0,L.useState)(null),tL=()=>{eA(!1),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)},tF=()=>{eA(!1),eF(null),e0(null),eT.resetFields(),eX([]),to([]),tc("llm_api"),tm({}),tg(!1),tx("30d"),tf(null),tS(e=>e+1),tA(null),e2(null),e3(null),t_([]),tv({}),tN(e=>e+1)};(0,L.useEffect)(()=>{eg&&eh&&ep&&en(eg,eh,ep,eP)},[ep,eg,eh]),(0,L.useEffect)(()=>{ep&&(0,et.getAgentsList)(ep).then(e=>tT(e?.agents||[])).catch(()=>tT([]))},[ep]),(0,L.useEffect)(()=>{let e=async()=>{try{let e=(await (0,et.getPoliciesList)(ep)).policies.map(e=>e.policy_name);eH(e)}catch(e){console.error("Failed to fetch policies:",e)}},t=async()=>{try{let e=await (0,et.getPromptsList)(ep);eJ(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}};(async()=>{try{let e=(await (0,et.getGuardrailsList)(ep)).guardrails.map(e=>e.guardrail_name);eq(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})(),e(),t()},[ep]),(0,L.useEffect)(()=>{(async()=>{try{if(ep){let e=sessionStorage.getItem("possibleUserRoles");if(e)te(JSON.parse(e));else{let e=await (0,et.getPossibleUserRoles)(ep);sessionStorage.setItem("possibleUserRoles",JSON.stringify(e)),te(e)}}}catch(e){console.error("Error fetching possible user roles:",e)}})()},[ep]),(0,L.useEffect)(()=>{if(eu&&!eV&&eo&&eh&&F.rolesWithWriteAccess.includes(eh)&&(eA(!0),ez(!0),em)){if(em.owned_by&&("another_user"===em.owned_by&&"Admin"!==eh?eD("you"):eD(em.owned_by)),em.team_id){let e=eo?.find(e=>e.team_id===em.team_id)||null;e&&(e0(e),eT.setFieldsValue({team_id:em.team_id}))}em.key_alias&&eT.setFieldsValue({key_alias:em.key_alias}),em.models&&em.models.length>0&&eG(em.models),em.key_type&&(tc(em.key_type),eT.setFieldsValue({key_type:em.key_type}))}},[eu,em,eo,eV,eT,eh]);let tM=eB.includes("no-default-models")&&!eZ,tO=async e=>{try{let t,l=e?.key_alias??"",a=e?.team_id??null;if((ed?.filter(e=>e.team_id===a).map(e=>e.key_alias)??[]).includes(l))throw Error(`Key alias ${l} already exists for team with ID ${a}, please provide another key alias`);if(ee.default.info("Making API Call"),eA(!0),"you"===eR)e.user_id=eg;else if("agent"===eR){if(!tI)return void ee.default.fromBackend("Please select an agent");e.agent_id=tI}let r={};try{r=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}if("service_account"===eR&&(r.service_account_id=e.key_alias),eY.length>0&&(r={...r,logging:eY.filter(e=>e.callback_name)}),tn.length>0){let e=(0,O.mapDisplayToInternalNames)(tn);r={...r,litellm_disabled_callbacks:e}}if(tp&&(e.auto_rotate=!0,e.rotation_interval=th),e.duration&&""!==e.duration.trim()||(e.duration=null),e.metadata=JSON.stringify(r),e.disable_global_guardrails||delete e.disable_global_guardrails,e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission={vector_stores:e.allowed_vector_store_ids},delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{servers:t,accessGroups:s}=e.allowed_mcp_servers_and_groups;t&&t.length>0&&(e.object_permission.mcp_servers=t),s&&s.length>0&&(e.object_permission.mcp_access_groups=s),delete e.allowed_mcp_servers_and_groups}let i=e.mcp_tool_permissions||{};if(Object.keys(i).length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_tool_permissions=i),delete e.mcp_tool_permissions,e.allowed_mcp_access_groups&&e.allowed_mcp_access_groups.length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_access_groups=e.allowed_mcp_access_groups,delete e.allowed_mcp_access_groups),e.allowed_agents_and_groups&&(e.allowed_agents_and_groups.agents?.length>0||e.allowed_agents_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{agents:t,accessGroups:s}=e.allowed_agents_and_groups;t&&t.length>0&&(e.object_permission.agents=t),s&&s.length>0&&(e.object_permission.agent_access_groups=s),delete e.allowed_agents_and_groups}Object.keys(tu).length>0&&(e.aliases=JSON.stringify(tu)),ty?.router_settings&&Object.values(ty.router_settings).some(e=>null!=e&&""!==e)&&(e.router_settings=ty.router_settings);let n=tb.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);n.length>0&&(e.budget_limits=n),Object.keys(tj).length>0&&(e.budget_fallbacks=tj),t="service_account"===eR?await (0,et.keyCreateServiceAccountCall)(ep,e):await (0,et.keyCreateCall)(ep,eg,e),ec(t),eC.invalidateQueries({queryKey:s.keyKeys.lists()}),eF(t.key),eO(t.soft_budget),ee.default.success("Virtual Key Created"),eT.resetFields(),t_([]),tv({}),tN(e=>e+1),localStorage.removeItem("userData"+eg)}catch(t){let e=(e=>{let t;if(!(t=!e||"object"!=typeof e||e instanceof Error?String(e):JSON.stringify(e)).includes("/key/generate")&&!t.includes("KeyManagementRoutes.KEY_GENERATE"))return`Error creating the key: ${e}`;let s=t;try{if(!e||"object"!=typeof e||e instanceof Error){let e=t.match(/\{[\s\S]*\}/);if(e){let t=JSON.parse(e[0]),l=t?.error||t;l?.message&&(s=l.message)}}else{let t=e?.error||e;t?.message&&(s=t.message)}}catch(e){}return t.includes("team_member_permission_error")||s.includes("Team member does not have permissions")?"Team member does not have permission to generate key for this team. Ask your proxy admin to configure the team member permission settings.":`Error creating the key: ${e}`})(t);ee.default.fromBackend(e)}};(0,L.useEffect)(()=>{if(e4){let e=e_?.find(e=>e.project_id===e4);e$(e?.models??[]),eT.setFieldValue("models",[]);return}eg&&eh&&ep&&ei(eg,eh,ep,eZ?.team_id??null).then(e=>{e$(Array.from(new Set([...eZ?.models??[],...e])))}),eU||eT.setFieldValue("models",[]),eT.setFieldValue("allowed_mcp_servers_and_groups",{servers:[],accessGroups:[]})},[eZ,e4,ep,eg,eh,eT]),(0,L.useEffect)(()=>{if(!eU||0===eU.length||!eB||0===eB.length)return;let e=eU.filter(e=>eB.includes(e));e.length>0&&eT.setFieldsValue({models:e}),eG(null)},[eU,eB,eT]),(0,L.useEffect)(()=>{if(!e4||!eo)return;let e=e_?.find(e=>e.project_id===e4);if(!e?.team_id||eZ?.team_id===e.team_id)return;let t=eo.find(t=>t.team_id===e.team_id)||null;t&&(e0(t),eT.setFieldValue("team_id",t.team_id))},[eo,e4,e_]);let tE=async e=>{if(!e)return void ts([]);ta(!0);try{let t=new URLSearchParams;if(t.append("user_email",e),null==ep)return;let s=(await (0,et.userFilterUICall)(ep,t)).map(e=>({label:`${e.user_email} (${e.user_id})`,value:e.user_id,user:e}));ts(s)}catch(e){console.error("Error fetching users:",e),ee.default.fromBackend("Failed to search for users")}finally{ta(!1)}},tP=(0,L.useCallback)((0,A.default)(e=>tE(e),300),[ep]);return(0,t.jsxs)("div",{children:[eh&&F.rolesWithWriteAccess.includes(eh)&&(0,t.jsx)(g.Button,{className:"mx-auto",onClick:()=>eA(!0),"data-testid":"create-key-button",children:"+ Create New Key"}),(0,t.jsx)(w.Modal,{open:eI,width:1e3,footer:null,onOk:tL,onCancel:tF,children:(0,t.jsxs)(j.Form,{form:eT,onFinish:tO,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Ownership"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Owned By"," ",(0,t.jsx)(T.Tooltip,{title:"Select who will own this Virtual Key",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),className:"mb-4",children:(0,t.jsxs)(N.Radio.Group,{onChange:e=>eD(e.target.value),value:eR,children:[(0,t.jsx)(N.Radio,{value:"you",children:"You"}),(0,t.jsx)(N.Radio,{value:"service_account",children:"Service Account"}),"Admin"===eh&&(0,t.jsx)(N.Radio,{value:"another_user",children:"Another User"}),(0,t.jsxs)(N.Radio,{value:"agent",children:["Agent ",(0,t.jsx)(C.Tag,{color:"purple",children:"New"})]})]})}),"another_user"===eR&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["User ID"," ",(0,t.jsx)(T.Tooltip,{title:"The user who will own this key and be responsible for its usage",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"user_id",className:"mt-4",rules:[{required:"another_user"===eR,message:"Please input the user ID of the user you are assigning the key to"}],children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",marginBottom:"8px"},children:[(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Type email to search for users",filterOption:!1,onSearch:e=>{tP(e)},onSelect:(e,t)=>{let s;return s=t.user,void eT.setFieldsValue({user_id:s.user_id})},options:tt,loading:tl,allowClear:!0,style:{width:"100%"},notFoundContent:tl?"Searching...":"No users found"}),(0,t.jsx)(_.Button,{onClick:()=>e6(!0),style:{marginLeft:"8px"},children:"Create User"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Search by email to find users"})]})}),"agent"===eR&&(0,t.jsxs)("div",{className:"mt-4 p-4 bg-purple-50 border border-purple-200 rounded-md",children:[(0,t.jsx)("div",{className:"mb-3",children:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:["Select Agent ",(0,t.jsx)("span",{className:"text-red-500",children:"*"})]})}),(0,t.jsx)(k.Select,{showSearch:!0,placeholder:"Select an agent",style:{width:"100%"},value:tI,onChange:e=>tA(e),filterOption:(e,t)=>t?.label?.toLowerCase().includes(e.toLowerCase()),options:tC.map(e=>({label:e.agent_name||e.agent_id,value:e.agent_id}))}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"This key will be used by the selected agent to make requests to LiteLLM"})]}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(T.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",className:"mt-4",children:(0,t.jsx)(K.default,{organizations:ef,loading:eb,disabled:"Admin"!==eh,onChange:e=>{e2(e||null),e0(null),e3(null),eT.setFieldValue("team_id",void 0),eT.setFieldValue("project_id",void 0)}})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Team"," ",(0,t.jsx)(T.Tooltip,{title:"The team this key belongs to, which determines available models and budget limits",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"team_id",initialValue:e?e.team_id:null,className:"mt-4",rules:[{required:"service_account"===eR,message:"Please select a team for the service account"}],help:"service_account"===eR?"required":"",children:(0,t.jsx)(G.default,{disabled:null!==e4,organizationId:e1,onTeamSelect:e=>{e0(e),e3(null),eT.setFieldValue("project_id",void 0),e?.organization_id?(e2(e.organization_id),eT.setFieldValue("organization_id",e.organization_id)):e||(e2(null),eT.setFieldValue("organization_id",void 0))}})}),eN&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Project"," ",(0,t.jsx)(T.Tooltip,{title:"Assign this key to a project. Selecting a project will lock the team to the project's team.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"project_id",className:"mt-4",children:(0,t.jsx)(q.default,{projects:e_,teamId:eZ?.team_id,loading:ej||!eo,onChange:e=>{if(!e){e3(null),e0(null),eT.setFieldValue("team_id",void 0);return}e3(e)}})})]}),tM&&(0,t.jsx)("div",{className:"mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md",children:(0,t.jsx)(y.Text,{className:"text-blue-800 text-sm",children:"Please select a team to continue configuring your Virtual Key. If you do not see any teams, please contact your Proxy Admin to either provide you with access to models or to add you to a team."})}),!tM&&(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Details"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["you"===eR||"another_user"===eR?"Key Name":"Service Account ID"," ",(0,t.jsx)(T.Tooltip,{title:"you"===eR||"another_user"===eR?"A descriptive name to identify this key":"Unique identifier for this service account",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_alias",rules:[{required:!0,message:`Please input a ${"you"===eR?"key name":"service account ID"}`}],help:"required",children:(0,t.jsx)(f.TextInput,{placeholder:""})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Models"," ",(0,t.jsx)(T.Tooltip,{title:"Select which models this key can access. Choose 'All Team Models' to grant access to all models available to the team. Leave empty to allow access to all models.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",rules:[],help:"management"===td||"read_only"===td?"Models field is disabled for this key type":"optional - leave empty to allow access to all models",className:"mt-4",children:(0,t.jsxs)(k.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:"management"===td||"read_only"===td,onChange:e=>{e.includes("all-team-models")&&eT.setFieldsValue({models:["all-team-models"]})},children:[!e4&&(0,t.jsx)(er,{value:"all-team-models",children:"All Team Models"},"all-team-models"),eB.map(e=>(0,t.jsx)(er,{value:e,children:(0,J.getModelDisplayName)(e)},e))]})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Key Type"," ",(0,t.jsx)(T.Tooltip,{title:"Select the type of key to determine what routes and operations this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_type",initialValue:"llm_api",className:"mt-4",children:(0,t.jsxs)(k.Select,{defaultValue:"llm_api",placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",onChange:e=>{tc(e),("management"===e||"read_only"===e)&&eT.setFieldsValue({models:[]})},children:[(0,t.jsx)(er,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"AI APIs"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(er,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Management"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only management routes (user/team/key management)"})]})}),(0,t.jsx)(er,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Full Access"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})})]})})]}),!tM&&(0,t.jsx)("div",{className:"mb-8",children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)(b.Title,{className:"m-0",children:"Optional Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum amount in USD this key can spend. When reached, the key will be blocked from making further requests",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",help:`Budget cannot exceed team max budget: $${e?.max_budget!==null&&e?.max_budget!==void 0?e?.max_budget:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.max_budget&&s>e.max_budget)throw Error(`Budget cannot exceed team max budget: $${(0,o.formatNumberWithCommas)(e.max_budget,4)}`)}}],children:(0,t.jsx)(el.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(T.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",help:`Team Reset Budget: ${e?.budget_duration!==null&&e?.budget_duration!==void 0?e?.budget_duration:"None"}`,children:(0,t.jsx)(P.default,{onChange:e=>eT.setFieldValue("budget_duration",e)})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(T.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(Q.BudgetWindowsEditor,{value:tb,onChange:t_})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(T.Tooltip,{title:"When a model exceeds its per-model budget (model_max_budget), requests automatically reroute to fallback models instead of failing. Configure per-model budgets in Advanced Settings.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(H.BudgetFallbacksEditor,{value:tj,onChange:tv,availableModels:eB},tw)}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Tokens per minute Limit (TPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of tokens this key can process per minute. Helps control usage and costs",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tpm_limit",help:`TPM cannot exceed team TPM limit: ${e?.tpm_limit!==null&&e?.tpm_limit!==void 0?e?.tpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.tpm_limit&&s>e.tpm_limit)throw Error(`TPM limit cannot exceed team TPM limit: ${e.tpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"tpm",name:"tpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Requests per minute Limit (RPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of API requests this key can make per minute. Helps prevent abuse and manage load",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"rpm_limit",help:`RPM cannot exceed team RPM limit: ${e?.rpm_limit!==null&&e?.rpm_limit!==void 0?e?.rpm_limit:"unlimited"}`,rules:[{validator:async(t,s)=>{if(s&&e&&null!==e.rpm_limit&&s>e.rpm_limit)throw Error(`RPM limit cannot exceed team RPM limit: ${e.rpm_limit}`)}}],children:(0,t.jsx)(el.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"rpm",name:"rpm_limit_type",className:"mt-4",initialValue:null,form:eT,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:ey?"Select existing guardrails or enter new ones":"Premium feature - Upgrade to set guardrails by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter guardrails":"Premium feature - Upgrade to set guardrails by key",options:eK.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"disable_global_guardrails",className:"mt-4",valuePropName:"checked",help:ey?"Bypass global guardrails for this key":"Premium feature - Upgrade to disable global guardrails by key",children:(0,t.jsx)(S.Switch,{disabled:!ey,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(T.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",className:"mt-4",help:ex?"Select existing policies or enter new ones":"Premium feature - Upgrade to set policies by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter policies":"Premium feature - Upgrade to set policies by key",options:eW.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Prompts"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific prompt templates",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/prompt_management",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"prompts",className:"mt-4",help:ex?"Select existing prompts or enter new ones":"Premium feature - Upgrade to set prompts by key",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},disabled:!ex,placeholder:ex?"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:eQ.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(T.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",className:"mt-4",help:"Select access groups to assign to this key",children:(0,t.jsx)(E.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Pass Through Routes"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific pass through routes",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"allowed_passthrough_routes",className:"mt-4",help:ex?"Select existing pass through routes or enter new ones":"Premium feature - Upgrade to set pass through routes by key",children:(0,t.jsx)(D.default,{onChange:e=>eT.setFieldValue("allowed_passthrough_routes",e),value:eT.getFieldValue("allowed_passthrough_routes"),accessToken:ep,placeholder:ex?"Select or enter pass through routes":"Premium feature - Upgrade to set pass through routes by key",disabled:!ex,teamId:eZ?eZ.team_id:null})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,t.jsx)(T.Tooltip,{title:"Select which vector stores this key can access. If none selected, the key will have access to all available vector stores",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this key can access. Leave empty for access to all vector stores",children:(0,t.jsx)(ea.default,{onChange:e=>eT.setFieldValue("allowed_vector_store_ids",e),value:eT.getFieldValue("allowed_vector_store_ids"),accessToken:ep,placeholder:"Select vector stores (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Metadata"," ",(0,t.jsx)(T.Tooltip,{title:"JSON object with additional information about this key. Used for tracking or custom logic",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"metadata",className:"mt-4",children:(0,t.jsx)(v.Input.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Tags"," ",(0,t.jsx)(T.Tooltip,{title:"Tags for tracking spend and/or doing tag-based routing. Used for analytics and filtering",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tags",className:"mt-4",help:"Tags for tracking spend and/or doing tag-based routing.",children:(0,t.jsx)(k.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",tokenSeparators:[","],options:eS})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"MCP Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(T.Tooltip,{title:"Select which MCP servers or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",help:"Select MCP servers or access groups this key can access",children:(0,t.jsx)(Y.default,{onChange:e=>eT.setFieldValue("allowed_mcp_servers_and_groups",e),value:eT.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:ep,teamId:eZ?.team_id??null,placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(j.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(j.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(Z.default,{accessToken:ep,selectedServers:(eT.getFieldValue("allowed_mcp_servers_and_groups")?.servers||[]).filter(e=>e!==X.NO_MCP_SERVERS_SENTINEL),toolPermissions:eT.getFieldValue("mcp_tool_permissions")||{},onChange:e=>eT.setFieldsValue({mcp_tool_permissions:e})})})})]})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Agent Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Agents"," ",(0,t.jsx)(T.Tooltip,{title:"Select which agents or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_agents_and_groups",help:"Select agents or access groups this key can access",children:(0,t.jsx)(M.default,{onChange:e=>eT.setFieldValue("allowed_agents_and_groups",e),value:eT.getFieldValue("allowed_agents_and_groups"),accessToken:ep,placeholder:"Select agents or access groups (optional)"})})})]}),ex?(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!0,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]}):(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Key-level logging settings is an enterprise feature, get in touch -",(0,t.jsx)("a",{href:"https://www.litellm.ai/enterprise",target:"_blank",children:"https://www.litellm.ai/enterprise"})]}),placement:"top",children:(0,t.jsxs)("div",{style:{position:"relative"},children:[(0,t.jsx)("div",{style:{opacity:.5},children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eY,onChange:eX,premiumUser:!1,disabledCallbacks:tn,onDisabledCallbacksChange:to})})})]})}),(0,t.jsx)("div",{style:{position:"absolute",inset:0,cursor:"not-allowed"}})]})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Router Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4 w-full",children:(0,t.jsx)(U.default,{accessToken:ep||"",value:ty||void 0,onChange:tf,modelData:eE.length>0?{data:eE.map(e=>({model_name:e}))}:void 0},tk)})})]},`router-settings-accordion-${tk}`),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Model Aliases"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)(y.Text,{className:"text-sm text-gray-600 mb-4",children:"Create custom aliases for models that can be used in API calls. This allows you to create shortcuts for specific models."}),(0,t.jsx)(R.default,{accessToken:ep,initialModelAliases:tu,onAliasUpdate:tm,showExampleConfig:!1})]})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Key Lifecycle"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)($.default,{form:eT,autoRotationEnabled:tp,onAutoRotationChange:tg,rotationInterval:th,onRotationIntervalChange:tx,isCreateMode:!0})})}),(0,t.jsx)(j.Form.Item,{name:"duration",hidden:!0,initialValue:null,children:(0,t.jsx)(v.Input,{})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("b",{children:"Advanced Settings"}),(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Learn more about advanced settings in our"," ",(0,t.jsx)("a",{href:et.proxyBaseUrl?`${et.proxyBaseUrl}/#/key%20management/generate_key_fn_key_generate_post`:"/#/key%20management/generate_key_fn_key_generate_post",target:"_blank",rel:"noopener noreferrer",className:"text-blue-400 hover:text-blue-300",children:"documentation"})]}),children:(0,t.jsx)(d.InfoCircleOutlined,{className:"text-gray-400 hover:text-gray-300 cursor-help"})})]})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(B.default,{schemaComponent:"GenerateKeyRequest",form:eT,excludedFields:["key_alias","team_id","organization_id","models","duration","metadata","tags","guardrails","max_budget","budget_duration","tpm_limit","rpm_limit",...ek?["key"]:[]]})})]})]})]})}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(_.Button,{htmlType:"submit",disabled:tM,style:{opacity:tM?.5:1},children:"Create Key"})})]})}),e5&&(0,t.jsx)(w.Modal,{title:"Create New User",open:e5,onCancel:()=>e6(!1),footer:null,width:800,children:(0,t.jsx)(W.CreateUserButton,{userID:eg,accessToken:ep,teams:eo,possibleUIRoles:e8,onUserCreated:e=>{e9(e),eT.setFieldsValue({user_id:e}),e6(!1)},isEmbedded:!0})}),eL&&(0,t.jsx)(w.Modal,{open:eI,onOk:tL,onCancel:tF,footer:null,children:(0,t.jsxs)(x.Grid,{numItems:1,className:"gap-2 w-full",children:[(0,t.jsx)(b.Title,{children:"Save your Key"}),(0,t.jsx)(h.Col,{numColSpan:1,children:null!=eL?(0,t.jsx)(es.default,{apiKey:eL}):(0,t.jsx)(y.Text,{children:"Key being created, this might take 30s"})})]})})]})},"fetchTeamModels",0,ei,"fetchUserModels",0,en],702597)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02jakvkccxpfw.js b/litellm/proxy/_experimental/out/_next/static/chunks/02jakvkccxpfw.js deleted file mode 100644 index 81de72e6c80..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/02jakvkccxpfw.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,916925,e=>{"use strict";var t,a=e.i(555987),l=((t={}).A2A_Agent="A2A Agent",t.AI21="Ai21",t.AI21_CHAT="Ai21 Chat",t.AIML="AI/ML API",t.AIOHTTP_OPENAI="Aiohttp Openai",t.Anthropic="Anthropic",t.ANTHROPIC_TEXT="Anthropic Text",t.AssemblyAI="AssemblyAI",t.AUTO_ROUTER="Auto Router",t.Bedrock="Amazon Bedrock",t.BedrockMantle="Amazon Bedrock Mantle",t.SageMaker="AWS SageMaker",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Foundry (Studio)",t.AZURE_TEXT="Azure Text",t.BASETEN="Baseten",t.BYTEZ="Bytez",t.Cerebras="Cerebras",t.CLARIFAI="Clarifai",t.CLOUDFLARE="Cloudflare",t.CODESTRAL="Codestral",t.Cohere="Cohere",t.COHERE_CHAT="Cohere Chat",t.COMETAPI="Cometapi",t.COMPACTIFAI="Compactifai",t.Cursor="Cursor",t.Dashscope="Dashscope",t.Databricks="Databricks (Qwen API)",t.DATAROBOT="Datarobot",t.DeepInfra="DeepInfra",t.Deepgram="Deepgram",t.Deepseek="Deepseek",t.DOCKER_MODEL_RUNNER="Docker Model Runner",t.DOTPROMPT="Dotprompt",t.ElevenLabs="ElevenLabs",t.EMPOWER="Empower",t.FalAI="Fal AI",t.FEATHERLESS_AI="Featherless Ai",t.FireworksAI="Fireworks AI",t.FRIENDLIAI="Friendliai",t.GALADRIEL="Galadriel",t.GITHUB_COPILOT="Github Copilot",t.Google_AI_Studio="Google AI Studio",t.GradientAI="GradientAI",t.Groq="Groq",t.HEROKU="Heroku",t.Hosted_Vllm="vllm",t.HUGGINGFACE="Huggingface",t.HYPERBOLIC="Hyperbolic",t.Infinity="Infinity",t.JinaAI="Jina AI",t.LAMBDA_AI="Lambda Ai",t.LEMONADE="Lemonade",t.LLAMAFILE="Llamafile",t.LM_STUDIO="Lm Studio",t.LLAMA="Meta Llama",t.MARITALK="Maritalk",t.MiniMax="MiniMax",t.MistralAI="Mistral AI",t.MOONSHOT="Moonshot",t.MORPH="Morph",t.NEBIUS="Nebius",t.NLP_CLOUD="Nlp Cloud",t.NOVITA="Novita",t.NSCALE="Nscale",t.NVIDIA_NIM="Nvidia Nim",t.Ollama="Ollama",t.OLLAMA_CHAT="Ollama Chat",t.OOBABOOGA="Oobabooga",t.OpenAI="OpenAI",t.OPENAI_LIKE="Openai Like",t.OpenAI_Compatible="OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)",t.OpenAI_Text="OpenAI Text Completion",t.OpenAI_Text_Compatible="OpenAI-Compatible Completions (legacy /v1/completions)",t.Openrouter="Openrouter",t.Oracle="Oracle Cloud Infrastructure (OCI)",t.OVHCLOUD="Ovhcloud",t.Perplexity="Perplexity",t.PETALS="Petals",t.PG_VECTOR="Pg Vector",t.PREDIBASE="Predibase",t.RECRAFT="Recraft",t.REPLICATE="Replicate",t.RunwayML="RunwayML",t.SAGEMAKER_LEGACY="Sagemaker",t.Sambanova="Sambanova",t.SAP="SAP Generative AI Hub",t.Snowflake="Snowflake",t.Soniox="Soniox",t.TEXT_COMPLETION_CODESTRAL="Text-Completion-Codestral",t.TogetherAI="TogetherAI",t.TOPAZ="Topaz",t.Triton="Triton",t.V0="V0",t.VERCEL_AI_GATEWAY="Vercel Ai Gateway",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.VERTEX_AI_BETA="Vertex Ai Beta",t.VLLM="Vllm",t.VolcEngine="VolcEngine",t.Voyage="Voyage AI",t.WANDB="Wandb",t.WATSONX="Watsonx",t.WATSONX_TEXT="Watsonx Text",t.xAI="xAI",t.XINFERENCE="Xinference",t.ZAI="Z.AI (Zhipu AI)",t);let n={A2A_Agent:"a2a_agent",AI21:"ai21",AI21_CHAT:"ai21_chat",AIML:"aiml",AIOHTTP_OPENAI:"aiohttp_openai",Anthropic:"anthropic",ANTHROPIC_TEXT:"anthropic_text",AssemblyAI:"assemblyai",AUTO_ROUTER:"auto_router",Azure:"azure",Azure_AI_Studio:"azure_ai",AZURE_TEXT:"azure_text",BASETEN:"baseten",Bedrock:"bedrock",BedrockMantle:"bedrock_mantle",BYTEZ:"bytez",Cerebras:"cerebras",CLARIFAI:"clarifai",CLOUDFLARE:"cloudflare",CODESTRAL:"codestral",Cohere:"cohere",COHERE_CHAT:"cohere_chat",COMETAPI:"cometapi",COMPACTIFAI:"compactifai",Cursor:"cursor",Dashscope:"dashscope",Databricks:"databricks",DATAROBOT:"datarobot",DeepInfra:"deepinfra",Deepgram:"deepgram",Deepseek:"deepseek",DOCKER_MODEL_RUNNER:"docker_model_runner",DOTPROMPT:"dotprompt",ElevenLabs:"elevenlabs",EMPOWER:"empower",FalAI:"fal_ai",FEATHERLESS_AI:"featherless_ai",FireworksAI:"fireworks_ai",FRIENDLIAI:"friendliai",GALADRIEL:"galadriel",GITHUB_COPILOT:"github_copilot",Google_AI_Studio:"gemini",GradientAI:"gradient_ai",Groq:"groq",HEROKU:"heroku",Hosted_Vllm:"hosted_vllm",HUGGINGFACE:"huggingface",HYPERBOLIC:"hyperbolic",Infinity:"infinity",JinaAI:"jina_ai",LAMBDA_AI:"lambda_ai",LEMONADE:"lemonade",LLAMAFILE:"llamafile",LLAMA:"meta_llama",LM_STUDIO:"lm_studio",MARITALK:"maritalk",MiniMax:"minimax",MistralAI:"mistral",MOONSHOT:"moonshot",MORPH:"morph",NEBIUS:"nebius",NLP_CLOUD:"nlp_cloud",NOVITA:"novita",NSCALE:"nscale",NVIDIA_NIM:"nvidia_nim",Ollama:"ollama",OLLAMA_CHAT:"ollama_chat",OOBABOOGA:"oobabooga",OpenAI:"openai",OPENAI_LIKE:"openai_like",OpenAI_Compatible:"openai",OpenAI_Text:"text-completion-openai",OpenAI_Text_Compatible:"text-completion-openai",Openrouter:"openrouter",Oracle:"oci",OVHCLOUD:"ovhcloud",Perplexity:"perplexity",PETALS:"petals",PG_VECTOR:"pg_vector",PREDIBASE:"predibase",RECRAFT:"recraft",REPLICATE:"replicate",RunwayML:"runwayml",SAGEMAKER_LEGACY:"sagemaker",SageMaker:"sagemaker_chat",Sambanova:"sambanova",SAP:"sap",Snowflake:"snowflake",Soniox:"soniox",TEXT_COMPLETION_CODESTRAL:"text-completion-codestral",TogetherAI:"together_ai",TOPAZ:"topaz",Triton:"triton",V0:"v0",VERCEL_AI_GATEWAY:"vercel_ai_gateway",Vertex_AI:"vertex_ai",VERTEX_AI_BETA:"vertex_ai_beta",VLLM:"vllm",VolcEngine:"volcengine",Voyage:"voyage",WANDB:"wandb",WATSONX:"watsonx",WATSONX_TEXT:"watsonx_text",xAI:"xai",XINFERENCE:"xinference",ZAI:"zai"},r=new Set(["bedrock_mantle"]),o="/ui/assets/logos/",i={"A2A Agent":`${o}a2a_agent.png`,Ai21:`${o}ai21.svg`,"Ai21 Chat":`${o}ai21.svg`,"AI/ML API":`${o}aiml_api.svg`,"Aiohttp Openai":`${o}openai_small.svg`,Anthropic:`${o}anthropic.svg`,"Anthropic Text":`${o}anthropic.svg`,AssemblyAI:`${o}assemblyai_small.png`,Azure:`${o}microsoft_azure.svg`,"Azure AI Foundry (Studio)":`${o}microsoft_azure.svg`,"Azure Text":`${o}microsoft_azure.svg`,Baseten:`${o}baseten.svg`,"Amazon Bedrock":`${o}bedrock.svg`,"Amazon Bedrock Mantle":`${o}bedrock.svg`,"AWS SageMaker":`${o}bedrock.svg`,Cerebras:`${o}cerebras.svg`,Cloudflare:`${o}cloudflare.svg`,Codestral:`${o}mistral.svg`,Cohere:`${o}cohere.svg`,"Cohere Chat":`${o}cohere.svg`,Cometapi:`${o}cometapi.svg`,Cursor:`${o}cursor.svg`,"Databricks (Qwen API)":`${o}databricks.svg`,Dashscope:`${o}dashscope.svg`,Deepseek:`${o}deepseek.svg`,Deepgram:`${o}deepgram.png`,DeepInfra:`${o}deepinfra.png`,ElevenLabs:`${o}elevenlabs.png`,"Fal AI":`${o}fal_ai.jpg`,"Featherless Ai":`${o}featherless.svg`,"Fireworks AI":`${o}fireworks.svg`,Friendliai:`${o}friendli.svg`,"Github Copilot":`${o}github_copilot.svg`,"Google AI Studio":`${o}google.svg`,GradientAI:`${o}gradientai.svg`,Groq:`${o}groq.svg`,vllm:`${o}vllm.png`,Huggingface:`${o}huggingface.svg`,Hyperbolic:`${o}hyperbolic.svg`,Infinity:`${o}infinity.png`,"Jina AI":`${o}jina.png`,"Lambda Ai":`${o}lambda.svg`,"Lm Studio":`${o}lmstudio.svg`,"Meta Llama":`${o}meta_llama.svg`,MiniMax:`${o}minimax.svg`,"Mistral AI":`${o}mistral.svg`,Moonshot:`${o}moonshot.svg`,Morph:`${o}morph.svg`,Nebius:`${o}nebius.svg`,Novita:`${o}novita.svg`,"Nvidia Nim":`${o}nvidia_nim.svg`,Ollama:`${o}ollama.svg`,"Ollama Chat":`${o}ollama.svg`,Oobabooga:`${o}openai_small.svg`,OpenAI:`${o}openai_small.svg`,"Openai Like":`${o}openai_small.svg`,"OpenAI Text Completion":`${o}openai_small.svg`,"OpenAI-Compatible Completions (legacy /v1/completions)":`${o}openai_small.svg`,"OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)":`${o}openai_small.svg`,Openrouter:`${o}openrouter.svg`,"Oracle Cloud Infrastructure (OCI)":`${o}oracle.svg`,Perplexity:`${o}perplexity-ai.svg`,Recraft:`${o}recraft.svg`,Replicate:`${o}replicate.svg`,RunwayML:`${o}runwayml.png`,Sagemaker:`${o}bedrock.svg`,Sambanova:`${o}sambanova.svg`,"SAP Generative AI Hub":`${o}sap.png`,Snowflake:`${o}snowflake.svg`,Soniox:`${o}soniox.svg`,"Text-Completion-Codestral":`${o}mistral.svg`,TogetherAI:`${o}togetherai.svg`,Topaz:`${o}topaz.svg`,Triton:`${o}nvidia_triton.png`,V0:`${o}v0.svg`,"Vercel Ai Gateway":`${o}vercel.svg`,"Vertex AI (Anthropic, Gemini, etc.)":`${o}google.svg`,"Vertex Ai Beta":`${o}google.svg`,Vllm:`${o}vllm.png`,VolcEngine:`${o}volcengine.png`,"Voyage AI":`${o}voyage.webp`,Watsonx:`${o}watsonx.svg`,"Watsonx Text":`${o}watsonx.svg`,xAI:`${o}xai.svg`,Xinference:`${o}xinference.svg`};e.s(["Providers",()=>l,"getPlaceholder",0,e=>{if("AI/ML API"===e)return"aiml/flux-pro/v1.1";if("Vertex AI (Anthropic, Gemini, etc.)"===e)return"gemini-pro";if("Anthropic"==e)return"claude-3-opus";if("Amazon Bedrock"==e)return"claude-3-opus";if("AWS SageMaker"==e)return"sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b";else if("Google AI Studio"==e)return"gemini-pro";else if("Azure AI Foundry (Studio)"==e)return"azure_ai/command-r-plus";else if("Azure"==e)return"my-deployment";else if("Oracle Cloud Infrastructure (OCI)"==e)return"oci/xai.grok-4";else if("Snowflake"==e)return"snowflake/mistral-7b";else if("Voyage AI"==e)return"voyage/";else if("Jina AI"==e)return"jina_ai/";else if("VolcEngine"==e)return"volcengine/";else if("DeepInfra"==e)return"deepinfra/";else if("Fal AI"==e)return"fal_ai/fal-ai/flux-pro/v1.1-ultra";else if("RunwayML"==e)return"runwayml/gen4_turbo";else if("Watsonx"===e)return"watsonx/ibm/granite-3-3-8b-instruct";else if("Cursor"===e)return"cursor/claude-4-sonnet";else if("Z.AI (Zhipu AI)"===e)return"zai/glm-4.5";else return"gpt-3.5-turbo"},"getProviderLogoAndName",0,e=>{if(!e)return{logo:"",displayName:"-"};if("gemini"===e.toLowerCase()){let e="Google AI Studio";return{logo:(0,a.resolveLogoSrc)(i[e])??"",displayName:e}}let t=Object.keys(n).find(t=>n[t].toLowerCase()===e.toLowerCase())??Object.keys(n).find(t=>t.toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let r=l[t];return{logo:(0,a.resolveLogoSrc)(i[r])??"",displayName:r}},"getProviderModels",0,(e,t)=>{let a=n[e],l=[];return e&&"object"==typeof t&&(Object.entries(t).forEach(([e,t])=>{if(null!==t&&"object"==typeof t&&"litellm_provider"in t){let n=t.litellm_provider,o="string"==typeof n&&(n.startsWith(`${a}_`)||n.startsWith(`${a}-`));(n===a||o&&!r.has(n))&&l.push(e)}}),"Cohere"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere_chat"===t.litellm_provider&&l.push(e)}),"AWS SageMaker"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"sagemaker_chat"===t.litellm_provider&&l.push(e)})),l},"providerLogoMap",0,i,"provider_map",0,n])},91979,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"}}]},name:"reload",theme:"outlined"};var n=e.i(9583),r=a.forwardRef(function(e,r){return a.createElement(n.default,(0,t.default)({},e,{ref:r,icon:l}))});e.s(["ReloadOutlined",0,r],91979)},608856,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),l=e.i(209428),n=e.i(392221),r=e.i(951160),o=e.i(174428),i=t.createContext(null),s=t.createContext({}),c=e.i(211577),u=e.i(931067),d=e.i(361275),m=e.i(404948),p=e.i(244009),f=e.i(703923),g=e.i(611935),h=["prefixCls","className","containerRef"];let v=function(e){var l=e.prefixCls,n=e.className,r=e.containerRef,o=(0,f.default)(e,h),i=t.useContext(s).panel,c=(0,g.useComposeRef)(i,r);return t.createElement("div",(0,u.default)({className:(0,a.default)("".concat(l,"-content"),n),role:"dialog",ref:c},(0,p.default)(e,{aria:!0}),{"aria-modal":"true"},o))};var A=e.i(883110);function b(e){return"string"==typeof e&&String(Number(e))===e?((0,A.default)(!1,"Invalid value type of `width` or `height` which should be number type instead."),Number(e)):e}var y={width:0,height:0,overflow:"hidden",outline:"none",position:"absolute"},x=t.forwardRef(function(e,r){var o,s,f,g=e.prefixCls,h=e.open,A=e.placement,x=e.inline,C=e.push,I=e.forceRender,w=e.autoFocus,O=e.keyboard,E=e.classNames,S=e.rootClassName,k=e.rootStyle,_=e.zIndex,L=e.className,$=e.id,T=e.style,M=e.motion,N=e.width,R=e.height,j=e.children,D=e.mask,P=e.maskClosable,z=e.maskMotion,H=e.maskClassName,B=e.maskStyle,F=e.afterOpenChange,V=e.onClose,G=e.onMouseEnter,K=e.onMouseOver,U=e.onMouseLeave,W=e.onClick,X=e.onKeyDown,q=e.onKeyUp,Q=e.styles,Y=e.drawerRender,Z=t.useRef(),J=t.useRef(),ee=t.useRef();t.useImperativeHandle(r,function(){return Z.current}),t.useEffect(function(){if(h&&w){var e;null==(e=Z.current)||e.focus({preventScroll:!0})}},[h]);var et=t.useState(!1),ea=(0,n.default)(et,2),el=ea[0],en=ea[1],er=t.useContext(i),eo=null!=(o=null!=(s=null==(f="boolean"==typeof C?C?{}:{distance:0}:C||{})?void 0:f.distance)?s:null==er?void 0:er.pushDistance)?o:180,ei=t.useMemo(function(){return{pushDistance:eo,push:function(){en(!0)},pull:function(){en(!1)}}},[eo]);t.useEffect(function(){var e,t;h?null==er||null==(e=er.push)||e.call(er):null==er||null==(t=er.pull)||t.call(er)},[h]),t.useEffect(function(){return function(){var e;null==er||null==(e=er.pull)||e.call(er)}},[]);var es=t.createElement(d.default,(0,u.default)({key:"mask"},z,{visible:D&&h}),function(e,n){var r=e.className,o=e.style;return t.createElement("div",{className:(0,a.default)("".concat(g,"-mask"),r,null==E?void 0:E.mask,H),style:(0,l.default)((0,l.default)((0,l.default)({},o),B),null==Q?void 0:Q.mask),onClick:P&&h?V:void 0,ref:n})}),ec="function"==typeof M?M(A):M,eu={};if(el&&eo)switch(A){case"top":eu.transform="translateY(".concat(eo,"px)");break;case"bottom":eu.transform="translateY(".concat(-eo,"px)");break;case"left":eu.transform="translateX(".concat(eo,"px)");break;default:eu.transform="translateX(".concat(-eo,"px)")}"left"===A||"right"===A?eu.width=b(N):eu.height=b(R);var ed={onMouseEnter:G,onMouseOver:K,onMouseLeave:U,onClick:W,onKeyDown:X,onKeyUp:q},em=t.createElement(d.default,(0,u.default)({key:"panel"},ec,{visible:h,forceRender:I,onVisibleChanged:function(e){null==F||F(e)},removeOnLeave:!1,leavedClassName:"".concat(g,"-content-wrapper-hidden")}),function(n,r){var o=n.className,i=n.style,s=t.createElement(v,(0,u.default)({id:$,containerRef:r,prefixCls:g,className:(0,a.default)(L,null==E?void 0:E.content),style:(0,l.default)((0,l.default)({},T),null==Q?void 0:Q.content)},(0,p.default)(e,{aria:!0}),ed),j);return t.createElement("div",(0,u.default)({className:(0,a.default)("".concat(g,"-content-wrapper"),null==E?void 0:E.wrapper,o),style:(0,l.default)((0,l.default)((0,l.default)({},eu),i),null==Q?void 0:Q.wrapper)},(0,p.default)(e,{data:!0})),Y?Y(s):s)}),ep=(0,l.default)({},k);return _&&(ep.zIndex=_),t.createElement(i.Provider,{value:ei},t.createElement("div",{className:(0,a.default)(g,"".concat(g,"-").concat(A),S,(0,c.default)((0,c.default)({},"".concat(g,"-open"),h),"".concat(g,"-inline"),x)),style:ep,tabIndex:-1,ref:Z,onKeyDown:function(e){var t,a,l=e.keyCode,n=e.shiftKey;switch(l){case m.default.TAB:l===m.default.TAB&&(n||document.activeElement!==ee.current?n&&document.activeElement===J.current&&(null==(a=ee.current)||a.focus({preventScroll:!0})):null==(t=J.current)||t.focus({preventScroll:!0}));break;case m.default.ESC:V&&O&&(e.stopPropagation(),V(e))}}},es,t.createElement("div",{tabIndex:0,ref:J,style:y,"aria-hidden":"true","data-sentinel":"start"}),em,t.createElement("div",{tabIndex:0,ref:ee,style:y,"aria-hidden":"true","data-sentinel":"end"})))});let C=function(e){var a=e.open,i=e.prefixCls,c=e.placement,u=e.autoFocus,d=e.keyboard,m=e.width,p=e.mask,f=void 0===p||p,g=e.maskClosable,h=e.getContainer,v=e.forceRender,A=e.afterOpenChange,b=e.destroyOnClose,y=e.onMouseEnter,C=e.onMouseOver,I=e.onMouseLeave,w=e.onClick,O=e.onKeyDown,E=e.onKeyUp,S=e.panelRef,k=t.useState(!1),_=(0,n.default)(k,2),L=_[0],$=_[1],T=t.useState(!1),M=(0,n.default)(T,2),N=M[0],R=M[1];(0,o.default)(function(){R(!0)},[]);var j=!!N&&void 0!==a&&a,D=t.useRef(),P=t.useRef();(0,o.default)(function(){j&&(P.current=document.activeElement)},[j]);var z=t.useMemo(function(){return{panel:S}},[S]);if(!v&&!L&&!j&&b)return null;var H=(0,l.default)((0,l.default)({},e),{},{open:j,prefixCls:void 0===i?"rc-drawer":i,placement:void 0===c?"right":c,autoFocus:void 0===u||u,keyboard:void 0===d||d,width:void 0===m?378:m,mask:f,maskClosable:void 0===g||g,inline:!1===h,afterOpenChange:function(e){var t,a;$(e),null==A||A(e),e||!P.current||null!=(t=D.current)&&t.contains(P.current)||null==(a=P.current)||a.focus({preventScroll:!0})},ref:D},{onMouseEnter:y,onMouseOver:C,onMouseLeave:I,onClick:w,onKeyDown:O,onKeyUp:E});return t.createElement(s.Provider,{value:z},t.createElement(r.default,{open:j||v||L,autoDestroy:!1,getContainer:h,autoLock:f&&(j||L)},t.createElement(x,H)))};var I=e.i(981444),w=e.i(617206),O=e.i(122767),E=e.i(613541),S=e.i(340010),k=e.i(242064),_=e.i(922611),L=e.i(563113),$=e.i(185793);let T=e=>{var l,n,r,o;let i,{prefixCls:s,ariaId:c,title:u,footer:d,extra:m,closable:p,loading:f,onClose:g,headerStyle:h,bodyStyle:v,footerStyle:A,children:b,classNames:y,styles:x}=e,C=(0,k.useComponentConfig)("drawer");i=!1===p?void 0:void 0===p||!0===p?"start":(null==p?void 0:p.placement)==="end"?"end":"start";let I=t.useCallback(e=>t.createElement("button",{type:"button",onClick:g,className:(0,a.default)(`${s}-close`,{[`${s}-close-${i}`]:"end"===i})},e),[g,s,i]),[w,O]=(0,L.useClosable)((0,L.pickClosable)(e),(0,L.pickClosable)(C),{closable:!0,closeIconRender:I});return t.createElement(t.Fragment,null,u||w?t.createElement("div",{style:Object.assign(Object.assign(Object.assign({},null==(r=C.styles)?void 0:r.header),h),null==x?void 0:x.header),className:(0,a.default)(`${s}-header`,{[`${s}-header-close-only`]:w&&!u&&!m},null==(o=C.classNames)?void 0:o.header,null==y?void 0:y.header)},t.createElement("div",{className:`${s}-header-title`},"start"===i&&O,u&&t.createElement("div",{className:`${s}-title`,id:c},u)),m&&t.createElement("div",{className:`${s}-extra`},m),"end"===i&&O):null,t.createElement("div",{className:(0,a.default)(`${s}-body`,null==y?void 0:y.body,null==(l=C.classNames)?void 0:l.body),style:Object.assign(Object.assign(Object.assign({},null==(n=C.styles)?void 0:n.body),v),null==x?void 0:x.body)},f?t.createElement($.default,{active:!0,title:!1,paragraph:{rows:5},className:`${s}-body-skeleton`}):b),(()=>{var e,l;if(!d)return null;let n=`${s}-footer`;return t.createElement("div",{className:(0,a.default)(n,null==(e=C.classNames)?void 0:e.footer,null==y?void 0:y.footer),style:Object.assign(Object.assign(Object.assign({},null==(l=C.styles)?void 0:l.footer),A),null==x?void 0:x.footer)},d)})())};e.i(296059);var M=e.i(915654),N=e.i(183293),R=e.i(246422),j=e.i(838378);let D=(e,t)=>({"&-enter, &-appear":Object.assign(Object.assign({},e),{"&-active":t}),"&-leave":Object.assign(Object.assign({},t),{"&-active":e})}),P=(e,t)=>Object.assign({"&-enter, &-appear, &-leave":{"&-start":{transition:"none"},"&-active":{transition:`all ${t}`}}},D({opacity:e},{opacity:1})),z=(0,R.genStyleHooks)("Drawer",e=>{let t=(0,j.mergeToken)(e,{});return[(e=>{let{borderRadiusSM:t,componentCls:a,zIndexPopup:l,colorBgMask:n,colorBgElevated:r,motionDurationSlow:o,motionDurationMid:i,paddingXS:s,padding:c,paddingLG:u,fontSizeLG:d,lineHeightLG:m,lineWidth:p,lineType:f,colorSplit:g,marginXS:h,colorIcon:v,colorIconHover:A,colorBgTextHover:b,colorBgTextActive:y,colorText:x,fontWeightStrong:C,footerPaddingBlock:I,footerPaddingInline:w,calc:O}=e,E=`${a}-content-wrapper`;return{[a]:{position:"fixed",inset:0,zIndex:l,pointerEvents:"none",color:x,"&-pure":{position:"relative",background:r,display:"flex",flexDirection:"column",[`&${a}-left`]:{boxShadow:e.boxShadowDrawerLeft},[`&${a}-right`]:{boxShadow:e.boxShadowDrawerRight},[`&${a}-top`]:{boxShadow:e.boxShadowDrawerUp},[`&${a}-bottom`]:{boxShadow:e.boxShadowDrawerDown}},"&-inline":{position:"absolute"},[`${a}-mask`]:{position:"absolute",inset:0,zIndex:l,background:n,pointerEvents:"auto"},[E]:{position:"absolute",zIndex:l,maxWidth:"100vw",transition:`all ${o}`,"&-hidden":{display:"none"}},[`&-left > ${E}`]:{top:0,bottom:0,left:{_skip_check_:!0,value:0},boxShadow:e.boxShadowDrawerLeft},[`&-right > ${E}`]:{top:0,right:{_skip_check_:!0,value:0},bottom:0,boxShadow:e.boxShadowDrawerRight},[`&-top > ${E}`]:{top:0,insetInline:0,boxShadow:e.boxShadowDrawerUp},[`&-bottom > ${E}`]:{bottom:0,insetInline:0,boxShadow:e.boxShadowDrawerDown},[`${a}-content`]:{display:"flex",flexDirection:"column",width:"100%",height:"100%",overflow:"auto",background:r,pointerEvents:"auto"},[`${a}-header`]:{display:"flex",flex:0,alignItems:"center",padding:`${(0,M.unit)(c)} ${(0,M.unit)(u)}`,fontSize:d,lineHeight:m,borderBottom:`${(0,M.unit)(p)} ${f} ${g}`,"&-title":{display:"flex",flex:1,alignItems:"center",minWidth:0,minHeight:0}},[`${a}-extra`]:{flex:"none"},[`${a}-close`]:Object.assign({display:"inline-flex",width:O(d).add(s).equal(),height:O(d).add(s).equal(),borderRadius:t,justifyContent:"center",alignItems:"center",color:v,fontWeight:C,fontSize:d,fontStyle:"normal",lineHeight:1,textAlign:"center",textTransform:"none",textDecoration:"none",background:"transparent",border:0,cursor:"pointer",transition:`all ${i}`,textRendering:"auto",[`&${a}-close-end`]:{marginInlineStart:h},[`&:not(${a}-close-end)`]:{marginInlineEnd:h},"&:hover":{color:A,backgroundColor:b,textDecoration:"none"},"&:active":{backgroundColor:y}},(0,N.genFocusStyle)(e)),[`${a}-title`]:{flex:1,margin:0,fontWeight:e.fontWeightStrong,fontSize:d,lineHeight:m},[`${a}-body`]:{flex:1,minWidth:0,minHeight:0,padding:u,overflow:"auto",[`${a}-body-skeleton`]:{width:"100%",height:"100%",display:"flex",justifyContent:"center"}},[`${a}-footer`]:{flexShrink:0,padding:`${(0,M.unit)(I)} ${(0,M.unit)(w)}`,borderTop:`${(0,M.unit)(p)} ${f} ${g}`},"&-rtl":{direction:"rtl"}}}})(t),(e=>{let{componentCls:t,motionDurationSlow:a}=e;return{[t]:{[`${t}-mask-motion`]:P(0,a),[`${t}-panel-motion`]:["left","right","top","bottom"].reduce((e,t)=>{let l;return Object.assign(Object.assign({},e),{[`&-${t}`]:[P(.7,a),D({transform:(l="100%",({left:`translateX(-${l})`,right:`translateX(${l})`,top:`translateY(-${l})`,bottom:`translateY(${l})`})[t])},{transform:"none"})]})},{})}}})(t)]},e=>({zIndexPopup:e.zIndexPopupBase,footerPaddingBlock:e.paddingXS,footerPaddingInline:e.padding}));var H=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,l=Object.getOwnPropertySymbols(e);nt.indexOf(l[n])&&Object.prototype.propertyIsEnumerable.call(e,l[n])&&(a[l[n]]=e[l[n]]);return a};let B={distance:180},F=e=>{let{rootClassName:l,width:n,height:r,size:o="default",mask:i=!0,push:s=B,open:c,afterOpenChange:u,onClose:d,prefixCls:m,getContainer:p,panelRef:f=null,style:h,className:v,"aria-labelledby":A,visible:b,afterVisibleChange:y,maskStyle:x,drawerStyle:L,contentWrapperStyle:$,destroyOnClose:M,destroyOnHidden:N}=e,R=H(e,["rootClassName","width","height","size","mask","push","open","afterOpenChange","onClose","prefixCls","getContainer","panelRef","style","className","aria-labelledby","visible","afterVisibleChange","maskStyle","drawerStyle","contentWrapperStyle","destroyOnClose","destroyOnHidden"]),j=(0,I.default)(),D=R.title?j:void 0,{getPopupContainer:P,getPrefixCls:F,direction:V,className:G,style:K,classNames:U,styles:W}=(0,k.useComponentConfig)("drawer"),X=F("drawer",m),[q,Q,Y]=z(X),Z=void 0===p&&P?()=>P(document.body):p,J=(0,a.default)({"no-mask":!i,[`${X}-rtl`]:"rtl"===V},l,Q,Y),ee=t.useMemo(()=>null!=n?n:"large"===o?736:378,[n,o]),et=t.useMemo(()=>null!=r?r:"large"===o?736:378,[r,o]),ea={motionName:(0,E.getTransitionName)(X,"mask-motion"),motionAppear:!0,motionEnter:!0,motionLeave:!0,motionDeadline:500},el=(0,_.usePanelRef)(),en=(0,g.composeRef)(f,el),[er,eo]=(0,O.useZIndex)("Drawer",R.zIndex),{classNames:ei={},styles:es={}}=R;return q(t.createElement(w.default,{form:!0,space:!0},t.createElement(S.default.Provider,{value:eo},t.createElement(C,Object.assign({prefixCls:X,onClose:d,maskMotion:ea,motion:e=>({motionName:(0,E.getTransitionName)(X,`panel-motion-${e}`),motionAppear:!0,motionEnter:!0,motionLeave:!0,motionDeadline:500})},R,{classNames:{mask:(0,a.default)(ei.mask,U.mask),content:(0,a.default)(ei.content,U.content),wrapper:(0,a.default)(ei.wrapper,U.wrapper)},styles:{mask:Object.assign(Object.assign(Object.assign({},es.mask),x),W.mask),content:Object.assign(Object.assign(Object.assign({},es.content),L),W.content),wrapper:Object.assign(Object.assign(Object.assign({},es.wrapper),$),W.wrapper)},open:null!=c?c:b,mask:i,push:s,width:ee,height:et,style:Object.assign(Object.assign({},K),h),className:(0,a.default)(G,v),rootClassName:J,getContainer:Z,afterOpenChange:null!=u?u:y,panelRef:en,zIndex:er,"aria-labelledby":null!=A?A:D,destroyOnClose:null!=N?N:M}),t.createElement(T,Object.assign({prefixCls:X},R,{ariaId:D,onClose:d}))))))};F._InternalPanelDoNotUseOrYouWillBeFired=e=>{let{prefixCls:l,style:n,className:r,placement:o="right"}=e,i=H(e,["prefixCls","style","className","placement"]),{getPrefixCls:s}=t.useContext(k.ConfigContext),c=s("drawer",l),[u,d,m]=z(c),p=(0,a.default)(c,`${c}-pure`,`${c}-${o}`,d,m,r);return u(t.createElement("div",{className:p,style:n},t.createElement(T,Object.assign({prefixCls:c},i))))},e.s(["Drawer",0,F],608856)},195116,e=>{"use strict";let t=(0,e.i(475254).default)("wrench",[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",key:"cbrjhi"}]]);e.s(["Wrench",0,t],195116)},149121,e=>{"use strict";var t=e.i(843476),a=e.i(271645),l=e.i(152990),n=e.i(682830),r=e.i(269200),o=e.i(427612),i=e.i(64848),s=e.i(942232),c=e.i(496020),u=e.i(977572);e.s(["DataTable",0,function({data:e=[],columns:d,onRowClick:m,renderSubComponent:p,renderChildRows:f,getRowCanExpand:g,isLoading:h=!1,loadingMessage:v="🚅 Loading logs...",noDataMessage:A="No logs found",enableSorting:b=!1}){let y=!!(p||f)&&!!g,x=d.some(e=>void 0!==e.size),[C,I]=(0,a.useState)([]),w=(0,l.useReactTable)({data:e,columns:d,...b&&{state:{sorting:C},onSortingChange:I,enableSortingRemoval:!1},...y&&{getRowCanExpand:g},getRowId:(e,t)=>e?.request_id??String(t),getCoreRowModel:(0,n.getCoreRowModel)(),...b&&{getSortedRowModel:(0,n.getSortedRowModel)()},...y&&{getExpandedRowModel:(0,n.getExpandedRowModel)()}}),O=x?{minWidth:w.getCenterTotalSize()}:{minWidth:"400px"};return(0,t.jsx)("div",{className:"rounded-lg custom-border overflow-x-auto w-full max-w-full box-border",children:(0,t.jsxs)(r.Table,{className:x?"[&_td]:py-0.5 [&_th]:py-1 [&_table]:table-fixed":"[&_td]:py-0.5 [&_th]:py-1 table-fixed w-full box-border",style:O,children:[(0,t.jsx)(o.TableHead,{children:w.getHeaderGroups().map(e=>(0,t.jsx)(c.TableRow,{children:e.headers.map(e=>{let a=b&&e.column.getCanSort(),n=e.column.getIsSorted();return(0,t.jsx)(i.TableHeaderCell,{className:`py-1 h-8 ${a?"cursor-pointer select-none hover:bg-gray-50":""}`,style:x?{width:e.getSize()}:void 0,onClick:a?e.column.getToggleSortingHandler():void 0,children:e.isPlaceholder?null:(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,l.flexRender)(e.column.columnDef.header,e.getContext()),a&&(0,t.jsx)("span",{className:"text-gray-400",children:"asc"===n?"↑":"desc"===n?"↓":"⇅"})]})},e.id)})},e.id))}),(0,t.jsx)(s.TableBody,{children:h?(0,t.jsx)(c.TableRow,{children:(0,t.jsx)(u.TableCell,{colSpan:d.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:v})})})}):w.getRowModel().rows.length>0?w.getRowModel().rows.map(e=>(0,t.jsxs)(a.Fragment,{children:[(0,t.jsx)(c.TableRow,{className:`h-8 ${m?"cursor-pointer hover:bg-gray-50":""}`,onClick:()=>m?.(e.original),children:e.getVisibleCells().map(e=>(0,t.jsx)(u.TableCell,{className:"py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap",style:x?{width:e.column.getSize()}:void 0,children:(0,l.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))}),y&&e.getIsExpanded()&&f&&f({row:e}),y&&e.getIsExpanded()&&p&&!f&&(0,t.jsx)(c.TableRow,{children:(0,t.jsx)(u.TableCell,{colSpan:e.getVisibleCells().length,className:"p-0",children:(0,t.jsx)("div",{className:"w-full max-w-full overflow-hidden box-border",children:p({row:e})})})})]},e.id)):(0,t.jsx)(c.TableRow,{children:(0,t.jsx)(u.TableCell,{colSpan:d.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:A})})})})})]})})}])},95684,e=>{"use strict";var t=e.i(165370);e.s(["Pagination",()=>t.default])},836991,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"}))});e.s(["XIcon",0,a],836991)},446891,e=>{"use strict";var t=e.i(843476),a=e.i(464571),l=e.i(326373),n=e.i(94629),r=e.i(360820),o=e.i(871943),i=e.i(836991);e.s(["TableHeaderSortDropdown",0,({sortState:e,onSortChange:s})=>{let c=[{key:"asc",label:"Ascending",icon:(0,t.jsx)(r.ChevronUpIcon,{className:"h-4 w-4"})},{key:"desc",label:"Descending",icon:(0,t.jsx)(o.ChevronDownIcon,{className:"h-4 w-4"})},{key:"reset",label:"Reset",icon:(0,t.jsx)(i.XIcon,{className:"h-4 w-4"})}];return(0,t.jsx)(l.Dropdown,{menu:{items:c,onClick:({key:e})=>{"asc"===e?s("asc"):"desc"===e?s("desc"):"reset"===e&&s(!1)},selectable:!0,selectedKeys:e?[e]:[]},trigger:["click"],autoAdjustOverflow:!0,children:(0,t.jsx)(a.Button,{type:"text",onClick:e=>e.stopPropagation(),icon:"asc"===e?(0,t.jsx)(r.ChevronUpIcon,{className:"h-4 w-4"}):"desc"===e?(0,t.jsx)(o.ChevronDownIcon,{className:"h-4 w-4"}):(0,t.jsx)(n.SwitchVerticalIcon,{className:"h-4 w-4"}),className:e?"text-blue-500 hover:text-blue-600":"text-gray-400 hover:text-blue-500"})})}])},362024,e=>{"use strict";var t=e.i(988122);e.s(["Collapse",()=>t.default])},240647,e=>{"use strict";var t=e.i(286612);e.s(["RightOutlined",()=>t.default])},245704,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"check-circle",theme:"outlined"};var n=e.i(9583),r=a.forwardRef(function(e,r){return a.createElement(n.default,(0,t.default)({},e,{ref:r,icon:l}))});e.s(["CheckCircleOutlined",0,r],245704)},149192,e=>{"use strict";var t=e.i(864517);e.s(["CloseOutlined",()=>t.default])},518617,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{"fill-rule":"evenodd",viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm0 76c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm128.01 198.83c.03 0 .05.01.09.06l45.02 45.01a.2.2 0 01.05.09.12.12 0 010 .07c0 .02-.01.04-.05.08L557.25 512l127.87 127.86a.27.27 0 01.05.06v.02a.12.12 0 010 .07c0 .03-.01.05-.05.09l-45.02 45.02a.2.2 0 01-.09.05.12.12 0 01-.07 0c-.02 0-.04-.01-.08-.05L512 557.25 384.14 685.12c-.04.04-.06.05-.08.05a.12.12 0 01-.07 0c-.03 0-.05-.01-.09-.05l-45.02-45.02a.2.2 0 01-.05-.09.12.12 0 010-.07c0-.02.01-.04.06-.08L466.75 512 338.88 384.14a.27.27 0 01-.05-.06l-.01-.02a.12.12 0 010-.07c0-.03.01-.05.05-.09l45.02-45.02a.2.2 0 01.09-.05.12.12 0 01.07 0c.02 0 .04.01.08.06L512 466.75l127.86-127.86c.04-.05.06-.06.08-.06a.12.12 0 01.07 0z"}}]},name:"close-circle",theme:"outlined"};var n=e.i(9583),r=a.forwardRef(function(e,r){return a.createElement(n.default,(0,t.default)({},e,{ref:r,icon:l}))});e.s(["CloseCircleOutlined",0,r],518617)},657150,e=>{"use strict";let t=(0,e.i(475254).default)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);e.s(["default",0,t])},531245,782273,793916,e=>{"use strict";var t=e.i(657150);e.s(["Bot",()=>t.default],531245),e.i(247167);var a=e.i(931067),l=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M625.9 115c-5.9 0-11.9 1.6-17.4 5.3L254 352H90c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h164l354.5 231.7c5.5 3.6 11.6 5.3 17.4 5.3 16.7 0 32.1-13.3 32.1-32.1V147.1c0-18.8-15.4-32.1-32.1-32.1zM586 803L293.4 611.7l-18-11.7H146V424h129.4l17.9-11.7L586 221v582zm348-327H806c-8.8 0-16 7.2-16 16v40c0 8.8 7.2 16 16 16h128c8.8 0 16-7.2 16-16v-40c0-8.8-7.2-16-16-16zm-41.9 261.8l-110.3-63.7a15.9 15.9 0 00-21.7 5.9l-19.9 34.5c-4.4 7.6-1.8 17.4 5.8 21.8L856.3 800a15.9 15.9 0 0021.7-5.9l19.9-34.5c4.4-7.6 1.7-17.4-5.8-21.8zM760 344a15.9 15.9 0 0021.7 5.9L892 286.2c7.6-4.4 10.2-14.2 5.8-21.8L878 230a15.9 15.9 0 00-21.7-5.9L746 287.8a15.99 15.99 0 00-5.8 21.8L760 344z"}}]},name:"sound",theme:"outlined"};var r=e.i(9583),o=l.forwardRef(function(e,t){return l.createElement(r.default,(0,a.default)({},e,{ref:t,icon:n}))});e.s(["SoundOutlined",0,o],782273);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M842 454c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8 0 140.3-113.7 254-254 254S258 594.3 258 454c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8 0 168.7 126.6 307.9 290 327.6V884H326.7c-13.7 0-24.7 14.3-24.7 32v36c0 4.4 2.8 8 6.2 8h407.6c3.4 0 6.2-3.6 6.2-8v-36c0-17.7-11-32-24.7-32H548V782.1c165.3-18 294-158 294-328.1zM512 624c93.9 0 170-75.2 170-168V232c0-92.8-76.1-168-170-168s-170 75.2-170 168v224c0 92.8 76.1 168 170 168zm-94-392c0-50.6 41.9-92 94-92s94 41.4 94 92v224c0 50.6-41.9 92-94 92s-94-41.4-94-92V232z"}}]},name:"audio",theme:"outlined"};var s=l.forwardRef(function(e,t){return l.createElement(r.default,(0,a.default)({},e,{ref:t,icon:i}))});e.s(["AudioOutlined",0,s],793916)},94629,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,a],94629)},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var n=e.i(9583),r=a.forwardRef(function(e,r){return a.createElement(n.default,(0,t.default)({},e,{ref:r,icon:l}))});e.s(["ArrowLeftOutlined",0,r],447566)},969550,e=>{"use strict";var t=e.i(843476),a=e.i(271645);let l=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"}))});var n=e.i(464571),r=e.i(311451),o=e.i(199133),i=e.i(374009);e.s(["default",0,({options:e,onApplyFilters:s,onResetFilters:c,initialValues:u={},buttonLabel:d="Filters"})=>{let[m,p]=(0,a.useState)(!1),[f,g]=(0,a.useState)(u),[h,v]=(0,a.useState)({}),[A,b]=(0,a.useState)({}),[y,x]=(0,a.useState)({}),[C,I]=(0,a.useState)({}),w=(0,a.useCallback)((0,i.default)(async(e,t)=>{if(t.isSearchable&&t.searchFn){b(e=>({...e,[t.name]:!0}));try{let a=await t.searchFn(e);v(e=>({...e,[t.name]:a}))}catch(e){console.error("Error searching:",e),v(e=>({...e,[t.name]:[]}))}finally{b(e=>({...e,[t.name]:!1}))}}},300),[]),O=(0,a.useCallback)(async e=>{if(e.isSearchable&&e.searchFn&&!e.loading&&!C[e.name]){b(t=>({...t,[e.name]:!0})),I(t=>({...t,[e.name]:!0}));try{let t=await e.searchFn("");v(a=>({...a,[e.name]:t}))}catch(t){console.error("Error loading initial options:",t),v(t=>({...t,[e.name]:[]}))}finally{b(t=>({...t,[e.name]:!1}))}}},[C]);(0,a.useEffect)(()=>{m&&e.forEach(e=>{e.isSearchable&&!C[e.name]&&O(e)})},[m,e,O,C]);let E=(e,t)=>{let a={...f,[e]:t};g(a),s(a)};return(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-6",children:[(0,t.jsx)(n.Button,{icon:(0,t.jsx)(l,{className:"h-4 w-4"}),onClick:()=>p(!m),className:"flex items-center gap-2",children:d}),(0,t.jsx)(n.Button,{onClick:()=>{let t={};e.forEach(e=>{t[e.name]=""}),g(t),c()},children:"Reset Filters"})]}),m&&(0,t.jsx)("div",{className:"grid grid-cols-3 gap-x-6 gap-y-4 mb-6",children:e.map(e=>{let a,l=A[e.name]||e.loading;return(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsx)("label",{className:"text-sm text-gray-600",children:e.label||e.name}),e.isSearchable?(0,t.jsx)(o.Select,{showSearch:!0,className:"w-full",placeholder:`Search ${e.label||e.name}...`,value:f[e.name]||void 0,onChange:t=>E(e.name,t),onOpenChange:t=>{t&&e.isSearchable&&!C[e.name]&&O(e)},onSearch:t=>{x(a=>({...a,[e.name]:t})),e.searchFn&&w(t,e)},filterOption:!1,loading:l,options:h[e.name]||[],allowClear:!0,notFoundContent:l?"Loading...":"No results found"}):e.options?(0,t.jsx)(o.Select,{className:"w-full",placeholder:`Select ${e.label||e.name}...`,value:f[e.name]||void 0,onChange:t=>E(e.name,t),allowClear:!0,children:e.options.map(e=>(0,t.jsx)(o.Select.Option,{value:e.value,children:e.label},e.value))}):e.customComponent?(a=e.customComponent,(0,t.jsx)(a,{value:f[e.name]||void 0,onChange:t=>E(e.name,t??""),placeholder:`Select ${e.label||e.name}...`,allFilters:f})):(0,t.jsx)(r.Input,{className:"w-full",placeholder:`Enter ${e.label||e.name}...`,value:f[e.name]||"",onChange:t=>E(e.name,t.target.value),allowClear:!0})]},e.name)})})]})}],969550)},50882,e=>{"use strict";var t=e.i(843476),a=e.i(621482),l=e.i(243652),n=e.i(602869),r=e.i(135214);let o=(0,l.createQueryKeys)("infiniteKeyAliases");var i=e.i(56456),s=e.i(152473),c=e.i(199133),u=e.i(271645);e.s(["PaginatedKeyAliasSelect",0,({value:e,onChange:l,placeholder:d="Select a key alias",style:m,pageSize:p=50,allowClear:f=!0,disabled:g=!1,allFilters:h})=>{let[v,A]=(0,u.useState)(""),[b,y]=(0,s.useDebouncedState)("",{wait:300}),{data:x,fetchNextPage:C,hasNextPage:I,isFetchingNextPage:w,isLoading:O}=((e=50,t,l)=>{let{accessToken:i}=(0,r.default)();return(0,a.useInfiniteQuery)({queryKey:o.list({filters:{size:e,...t&&{search:t},...l&&{team_id:l}}}),queryFn:async({pageParam:a})=>await (0,n.keyAliasesCall)(i,a,e,t,l),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{if(!x?.pages)return[];let e=new Set,t=[];for(let a of x.pages)for(let l of a.aliases)!l||e.has(l)||(e.add(l),t.push({label:l,value:l}));return t},[x]);return(0,t.jsx)(c.Select,{value:e||void 0,onChange:e=>{l?.(e??"")},placeholder:d,style:{width:"100%",...m},allowClear:f,disabled:g,showSearch:!0,filterOption:!1,onSearch:e=>{A(e),y(e)},searchValue:v,onPopupScroll:e=>{let t=e.currentTarget;(t.scrollTop+t.clientHeight)/t.scrollHeight>=.8&&I&&!w&&C()},loading:O,notFoundContent:O?(0,t.jsx)(i.LoadingOutlined,{spin:!0}):"No key aliases found",options:E,popupRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,w&&(0,t.jsx)("div",{style:{textAlign:"center",padding:8},children:(0,t.jsx)(i.LoadingOutlined,{spin:!0})})]})})}],50882)},307582,e=>{"use strict";var t=e.i(843476);e.s(["TimeCell",0,({utcTime:e})=>(0,t.jsx)("span",{style:{fontFamily:"monospace",width:"180px",display:"inline-block"},children:(e=>{try{return new Date(e).toLocaleString("en-US",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!0}).replace(",","")}catch(e){return"Error converting time"}})(e)})])},700514,e=>{"use strict";var t=e.i(271645);e.s(["defaultPageSize",0,25,"useBaseUrl",0,()=>{let[e,a]=(0,t.useState)("http://localhost:4000");return(0,t.useEffect)(()=>{{let{protocol:e,host:t}=window.location;a(`${e}//${t}`)}},[]),e}])},625901,e=>{"use strict";var t=e.i(266027),a=e.i(621482),l=e.i(243652),n=e.i(602869),r=e.i(135214);let o=(0,l.createQueryKeys)("models"),i=(0,l.createQueryKeys)("modelHub"),s=(0,l.createQueryKeys)("allProxyModels");(0,l.createQueryKeys)("selectedTeamModels");let c=(0,l.createQueryKeys)("infiniteModels"),u=(0,l.createQueryKeys)("userModels");e.s(["useAllProxyModels",0,()=>{let{accessToken:e,userId:a,userRole:l}=(0,r.default)();return(0,t.useQuery)({queryKey:s.list({}),queryFn:async()=>await (0,n.modelAvailableCall)(e,a,l,!0,null,!0,!1,"expand"),enabled:!!(e&&a&&l)})},"useInfiniteModelInfo",0,(e=50,t)=>{let{accessToken:l,userId:o,userRole:i}=(0,r.default)();return(0,a.useInfiniteQuery)({queryKey:c.list({filters:{...o&&{userId:o},...i&&{userRole:i},size:e,...t&&{search:t}}}),queryFn:async({pageParam:a})=>await (0,n.modelInfoCall)(l,o,i,a,e,t),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{let{accessToken:e}=(0,r.default)();return(0,t.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,n.modelHubCall)(e),enabled:!!e})},"useModelsInfo",0,(e=1,a=50,l,i,s,c,u)=>{let{accessToken:d,userId:m,userRole:p}=(0,r.default)();return(0,t.useQuery)({queryKey:o.list({filters:{...m&&{userId:m},...p&&{userRole:p},page:e,size:a,...l&&{search:l},...i&&{modelId:i},...s&&{teamId:s},...c&&{sortBy:c},...u&&{sortOrder:u}}}),queryFn:async()=>await (0,n.modelInfoCall)(d,m,p,e,a,l,i,s,c,u),enabled:!!(d&&m&&p)})},"useUserModels",0,()=>{let{accessToken:e,userId:a,userRole:l}=(0,r.default)();return(0,t.useQuery)({queryKey:u.list({}),queryFn:async()=>(await (0,n.modelAvailableCall)(e,a,l)).data.map(e=>e.id),enabled:!!(e&&a&&l)})}])},633627,e=>{"use strict";var t=e.i(602869);let a=(e,t,a,l)=>{for(let n of e){let e=n?.key_alias;e&&"string"==typeof e&&t.add(e.trim());let r=n?.organization_id??n?.org_id;r&&"string"==typeof r&&a.add(r.trim());let o=n?.user_id;if(o&&"string"==typeof o){let e=n?.user?.user_email||o;l.set(o,e)}}},l=async(e,l)=>{if(!e||!l)return{keyAliases:[],organizationIds:[],userIds:[]};try{let n=new Set,r=new Set,o=new Map,i=await (0,t.keyListCall)(e,null,l,null,null,null,1,100,null,null,"user",null),s=i?.keys||[],c=i?.total_pages??1;a(s,n,r,o);let u=Math.min(c,10)-1;if(u>0){let i=Array.from({length:u},(a,n)=>(0,t.keyListCall)(e,null,l,null,null,null,n+2,100,null,null,"user",null));for(let e of(await Promise.allSettled(i)))"fulfilled"===e.status&&a(e.value?.keys||[],n,r,o)}return{keyAliases:Array.from(n).sort(),organizationIds:Array.from(r).sort(),userIds:Array.from(o.entries()).map(([e,t])=>({id:e,email:t}))}}catch(e){return console.error("Error fetching team filter options:",e),{keyAliases:[],organizationIds:[],userIds:[]}}},n=async(e,a)=>{if(!e)return[];try{let l=[],n=1,r=!0;for(;r;){let o=await (0,t.teamListCall)(e,a||null,null);l=[...l,...o],n{"use strict";var t=e.i(843476),a=e.i(482725),l=e.i(56456);e.s(["AntDLoadingSpinner",0,function({size:e,fontSize:n}){let r=(0,t.jsx)(l.LoadingOutlined,{style:n?{fontSize:n}:void 0,spin:!0});return(0,t.jsx)(a.Spin,{indicator:r,size:e})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02u6qkt2tomg4.js b/litellm/proxy/_experimental/out/_next/static/chunks/02u6qkt2tomg4.js new file mode 100644 index 00000000000..175f9aa4611 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02u6qkt2tomg4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),o=e.i(480731),n=e.i(444755),s=e.i(673706),i=e.i(95779);let l={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},u={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},c=(0,s.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:p,variant:f="simple",tooltip:g,size:h=o.Sizes.SM,color:b,className:v}=e,x=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),C=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,s.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(f,b),{tooltipProps:y,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,s.mergeRefs)([m,y.refs.setReference]),className:(0,n.tremorTwMerge)(c("root"),"inline-flex shrink-0 items-center justify-center",C.bgColor,C.textColor,C.borderColor,C.ringColor,u[f].rounded,u[f].border,u[f].shadow,u[f].ring,l[h].paddingX,l[h].paddingY,v)},w,x),r.default.createElement(a.default,Object.assign({text:g},y)),r.default.createElement(p,{className:(0,n.tremorTwMerge)(c("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),a=e.i(122577),o=e.i(278587),n=e.i(68155),s=e.i(360820),i=e.i(871943),l=e.i(434626),d=e.i(551332),u=e.i(592968),c=e.i(115504),m=e.i(752978);function p({icon:e,onClick:r,className:a,disabled:o,dataTestId:n}){return o?(0,t.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":n}):(0,t.jsx)(m.Icon,{icon:e,size:"sm",onClick:r,className:(0,c.cx)("cursor-pointer",a),"data-testid":n})}let f={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:n.TrashIcon,className:"hover:text-red-600"},Test:{icon:a.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:o.RefreshIcon,className:"hover:text-green-600"},Up:{icon:s.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:i.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:l.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:a=!1,disabledTooltipText:o,dataTestId:n,variant:s}){let{icon:i,className:l}=f[s];return(0,t.jsx)(u.Tooltip,{title:a?o:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(p,{icon:i,onClick:e,className:l,disabled:a,dataTestId:n})})})}],902555)},564897,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M696 480H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"minus-circle",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["MinusCircleOutlined",0,n],564897)},91979,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"}}]},name:"reload",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ReloadOutlined",0,n],91979)},751904,e=>{"use strict";var t=e.i(401361);e.s(["EditOutlined",()=>t.default])},678784,e=>{"use strict";var t=e.i(678745);e.s(["CheckIcon",()=>t.default])},118366,e=>{"use strict";var t=e.i(991124);e.s(["CopyIcon",()=>t.default])},987432,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M893.3 293.3L730.7 130.7c-7.5-7.5-16.7-13-26.7-16V112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V338.5c0-17-6.7-33.2-18.7-45.2zM384 184h256v104H384V184zm456 656H184V184h136v136c0 17.7 14.3 32 32 32h320c17.7 0 32-14.3 32-32V205.8l136 136V840zM512 442c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144zm0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z"}}]},name:"save",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["SaveOutlined",0,n],987432)},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},54131,399219,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-up",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);e.s(["default",0,t],399219),e.s(["ChevronUpIcon",0,t],54131)},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(281256).Row;e.s(["Row",0,t],621192)},263147,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),o=e.i(431703),n=e.i(708347),s=e.i(135214);let i=(0,r.createQueryKeys)("accessGroups"),l=async e=>{let t=(0,a.getProxyBaseUrl)(),r=`${t}/v1/access_group`,n=await fetch(r,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!n.ok){let e=await n.json(),t=(0,o.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return n.json()};e.s(["accessGroupKeys",0,i,"useAccessGroups",0,()=>{let{accessToken:e,userRole:r}=(0,s.default)();return(0,t.useQuery)({queryKey:i.list({}),queryFn:async()=>l(e),enabled:!!e&&n.all_admin_roles.includes(r||"")})}])},304911,e=>{"use strict";var t=e.i(843476),r=e.i(262218);let{Text:a}=e.i(898586).Typography;e.s(["default",0,function({userId:e}){return"default_user_id"===e?(0,t.jsx)(r.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(a,{children:e})}])},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ArrowLeftOutlined",0,n],447566)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},637235,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"}}]},name:"clock-circle",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ClockCircleOutlined",0,n],637235)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["default",0,n],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],n=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let s=n(e);t(s),r.current=s,o&&o({current:s})};var l=e.i(480731),d=e.i(444755),u=e.i(673706);let c=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let p={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},f=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,u.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,u.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,u.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,u.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,u.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,u.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,u.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,u.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,u.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,u.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,u.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,u.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,u.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,u.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},g=(0,u.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:n,transitionStatus:s})=>{let i=n?r===l.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",u=(0,d.tremorTwMerge)("w-0 h-0"),m={default:u,entering:u,entered:t,exiting:t,exited:u};return e?a.default.createElement(c,{className:(0,d.tremorTwMerge)(g("icon"),"animate-spin shrink-0",i,m.default,m[s]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(g("icon"),"shrink-0",t,i)})},b=a.default.forwardRef((e,o)=>{let{icon:c,iconPosition:m=l.HorizontalPositions.Left,size:b=l.Sizes.SM,color:v,variant:x="primary",disabled:C,loading:y=!1,loadingText:w,children:S,tooltip:k,className:N}=e,M=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),D=y||C,O=void 0!==c||y,E=y&&w,R=!(!S&&!E),P=(0,d.tremorTwMerge)(p[b].height,p[b].width),T="light"!==x?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",j=f(x,v),I=("light"!==x?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:_,getReferenceProps:$}=(0,r.useTooltip)(300),[B,z]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:l,initialEntered:d,mountOnEnter:u,unmountOnExit:c,onStateChange:m}={})=>{let[p,f]=(0,a.useState)(()=>n(d?2:s(u))),g=(0,a.useRef)(p),h=(0,a.useRef)(0),[b,v]="object"==typeof l?[l.enter,l.exit]:[l,l],x=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(t)}})(g.current._s,c);e&&i(e,f,g,h,m)},[m,c]);return[p,(0,a.useCallback)(a=>{let n=e=>{switch(i(e,f,g,h,m),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(x,b));break;case 4:v>=0&&(h.current=((...e)=>setTimeout(...e))(x,v));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||n(e+1)},0)}},l=g.current.isEnter;"boolean"!=typeof a&&(a=!l),a?l||n(e?+!r:2):l&&n(t?o?3:4:s(c))},[x,m,e,t,r,o,b,v,c]),x]})({timeout:50});return(0,a.useEffect)(()=>{z(y)},[y]),a.default.createElement("button",Object.assign({ref:(0,u.mergeRefs)([o,_.refs.setReference]),className:(0,d.tremorTwMerge)(g("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",T,I.paddingX,I.paddingY,I.fontSize,j.textColor,j.bgColor,j.borderColor,j.hoverBorderColor,D?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(f(x,v).hoverTextColor,f(x,v).hoverBgColor,f(x,v).hoverBorderColor),N),disabled:D},$,M),a.default.createElement(r.default,Object.assign({text:k},_)),O&&m!==l.HorizontalPositions.Right?a.default.createElement(h,{loading:y,iconSize:P,iconPosition:m,Icon:c,transitionStatus:B.status,needMargin:R}):null,E||S?a.default.createElement("span",{className:(0,d.tremorTwMerge)(g("text"),"text-tremor-default whitespace-nowrap")},E?w:S):null,O&&m===l.HorizontalPositions.Right?a.default.createElement(h,{loading:y,iconSize:P,iconPosition:m,Icon:c,transitionStatus:B.status,needMargin:R}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),o=e.i(271645);let n=o.default.forwardRef((e,n)=>{let{color:s,className:i,children:l}=e;return o.default.createElement("p",{ref:n,className:(0,r.tremorTwMerge)("text-tremor-default",s?(0,a.getColorClassNames)(s,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},l)});n.displayName="Text",e.s(["default",0,n],936325),e.s(["Text",0,n],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),n=e.i(444755),s=e.i(673706);let i=(0,s.makeClassName)("Card"),l=r.default.forwardRef((e,l)=>{let{decoration:d="",decorationColor:u,children:c,className:m}=e,p=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:l,className:(0,n.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",u?(0,s.getColorClassNames)(u,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},p),c)});l.displayName="Card",e.s(["Card",0,l],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),o=e.i(673706),n=e.i(271645);let s=n.default.forwardRef((e,s)=>{let{color:i,children:l,className:d}=e,u=(0,t.__rest)(e,["color","children","className"]);return n.default.createElement("p",Object.assign({ref:s,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",i?(0,o.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},u),l)});s.displayName="Title",e.s(["Title",0,s],629569)},891547,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),o=e.i(602869);e.s(["default",0,({onChange:e,value:n,className:s,accessToken:i,disabled:l})=>{let[d,u]=(0,r.useState)([]),[c,m]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(i){m(!0);try{let e=await (0,o.getGuardrailsList)(i);e.guardrails&&u(e.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{m(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:l,placeholder:l?"Setting guardrails is a premium feature.":"Select guardrails",onChange:t=>{e(t)},value:n,loading:c,className:s,allowClear:!0,options:d.map(e=>({label:`${e.guardrail_name}`,value:e.guardrail_name})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})}])},921511,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),o=e.i(602869);function n(e){return e.filter(e=>(e.version_status??"draft")!=="draft").map(e=>{var t;let r=e.version_number??1,a=e.version_status??"draft";return{label:`${e.policy_name} — v${r} (${a})${e.description?` — ${e.description}`:""}`,value:"production"===a?e.policy_name:e.policy_id?(t=e.policy_id,`policy_${t}`):e.policy_name}})}e.s(["default",0,({onChange:e,value:s,className:i,accessToken:l,disabled:d,onPoliciesLoaded:u})=>{let[c,m]=(0,r.useState)([]),[p,f]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(l){f(!0);try{let e=await (0,o.getPoliciesList)(l);e.policies&&(m(e.policies),u?.(e.policies))}catch(e){console.error("Error fetching policies:",e)}finally{f(!1)}}})()},[l,u]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:d,placeholder:d?"Setting policies is a premium feature.":"Select policies (production or published versions)",onChange:t=>{e(t)},value:s,loading:p,className:i,allowClear:!0,options:n(c),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})},"getPolicyOptionEntries",0,n])},595727,234662,677241,281092,688594,e=>{"use strict";let t=Symbol.for("constructDateFrom");function r(e,r){return"function"==typeof e?e(r):e&&"object"==typeof e&&t in e?e[t](r):e instanceof Date?new e.constructor(r):new Date(r)}function a(e,t){return r(t||e,e)}e.s(["constructFromSymbol",0,t,"millisecondsInDay",0,864e5,"millisecondsInHour",0,36e5,"millisecondsInMinute",0,6e4,"millisecondsInSecond",0,1e3,"millisecondsInWeek",0,6048e5],234662),e.s(["constructFrom",0,r],677241),e.s(["toDate",0,a],281092),e.s(["addDays",0,function(e,t,o){let n=a(e,o?.in);return isNaN(t)?r(o?.in||e,NaN):(t&&n.setDate(n.getDate()+t),n)}],595727),e.s(["addMonths",0,function(e,t,o){let n=a(e,o?.in);if(isNaN(t))return r(o?.in||e,NaN);if(!t)return n;let s=n.getDate(),i=r(o?.in||e,n.getTime());return(i.setMonth(n.getMonth()+t+1,0),s>=i.getDate())?i:(n.setFullYear(i.getFullYear(),i.getMonth(),s),n)}],688594)},24529,e=>{"use strict";var t=e.i(595727),r=e.i(688594),a=e.i(677241),o=e.i(281092);function n(e,n,s){let{years:i=0,months:l=0,weeks:d=0,days:u=0,hours:c=0,minutes:m=0,seconds:p=0}=n,f=(0,o.toDate)(e,s?.in),g=l||i?(0,r.addMonths)(f,l+12*i):f,h=u||d?(0,t.addDays)(g,u+7*d):g;return(0,a.constructFrom)(s?.in||e,+h+1e3*(p+60*(m+60*c)))}let s=/[zZ]$|[+-]\d{2}:?\d{2}$/;function i(e){return Date.parse(s.test(e)?e:`${e}Z`)}e.s(["calculateExpiryPreviewFromDuration",0,function(e){if(!e)return null;try{let t,r=parseInt(e);if(Number.isNaN(r))throw Error("Invalid duration format");let a=new Date;if(e.endsWith("mo"))t=n(a,{months:r});else if(e.endsWith("s"))t=n(a,{seconds:r});else if(e.endsWith("m"))t=n(a,{minutes:r});else if(e.endsWith("h"))t=n(a,{hours:r});else if(e.endsWith("d"))t=n(a,{days:r});else if(e.endsWith("w"))t=n(a,{weeks:r});else throw Error("Invalid duration format");return t.toLocaleString()}catch{return null}},"formatExpiresUtc",0,function(e){let t=i(e);return Number.isNaN(t)?e:new Date(t).toLocaleString()},"isKeyExpired",0,function(e){if(!e)return!1;let t=i(e);return!Number.isNaN(t)&&t{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"}}]},name:"thunderbolt",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ThunderboltOutlined",0,n],962944)},72713,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"}}]},name:"calendar",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["CalendarOutlined",0,n],72713)},140928,e=>{"use strict";var t=e.i(271645),r=e.i(152473);e.s(["useDebouncedValue",0,function(e,a){let[o,n,s]=(0,r.useDebouncedState)(e,a);return(0,t.useEffect)(()=>(n(e),()=>{s.cancel()}),[e,n,s]),[o,s]}])},995926,e=>{"use strict";var t=e.i(841947);e.s(["XIcon",()=>t.default])},108821,e=>{"use strict";var t=e.i(733332),r=e.i(271645);let a=r.createContext(!1),o=r.createContext(void 0);e.s(["DialogRootContext",0,o,"IsDrawerContext",0,a,"useDialogRootContext",0,function(e){let a=r.useContext(o);if(!1===e&&void 0===a)throw Error((0,t.default)(27));return a}])},402820,156736,209793,625834,784324,264951,e=>{"use strict";e.i(247167);var t,r,a=e.i(271645),o=e.i(108821),n=e.i(552245),s=e.i(405005),i=e.i(209407);let l={...s.popupStateMapping,...i.transitionStatusMapping},d=a.forwardRef(function(e,t){let{render:r,className:a,style:s,forceRender:i=!1,...d}=e,{store:u}=(0,o.useDialogRootContext)(),c=u.useState("open"),m=u.useState("nested"),p=u.useState("mounted"),f=u.useState("transitionStatus");return(0,n.useRenderElement)("div",e,{state:{open:c,transitionStatus:f},ref:[u.context.backdropRef,t],stateAttributesMapping:l,props:[{role:"presentation",hidden:!p,style:{userSelect:"none",WebkitUserSelect:"none"}},d],enabled:i||!m})});e.s(["DialogBackdrop",0,d],402820);var u=e.i(540886),c=e.i(675606),m=e.i(56434);let p=a.forwardRef(function(e,t){let{render:r,className:a,style:s,disabled:i=!1,nativeButton:l=!0,...d}=e,{store:p}=(0,o.useDialogRootContext)(),f=p.useState("open"),{getButtonProps:g,buttonRef:h}=(0,u.useButton)({disabled:i,native:l});return(0,n.useRenderElement)("button",e,{state:{disabled:i},ref:[t,h],props:[{onClick:function(e){f&&p.setOpen(!1,(0,c.createChangeEventDetails)(m.REASONS.closePress,e.nativeEvent))}},d,g]})});e.s(["DialogClose",0,p],156736);var f=e.i(788015);let g=a.forwardRef(function(e,t){let{render:r,className:a,style:s,id:i,...l}=e,{store:d}=(0,o.useDialogRootContext)(),u=(0,f.useBaseUiId)(i);return d.useSyncedValueWithCleanup("descriptionElementId",u),(0,n.useRenderElement)("p",e,{ref:t,props:[{id:u},l]})});e.s(["DialogDescription",0,g],209793);var h=e.i(61487);let b=((t={}).nestedDialogs="--nested-dialogs",t),v=((r={})[r.open=s.CommonPopupDataAttributes.open]="open",r[r.closed=s.CommonPopupDataAttributes.closed]="closed",r[r.startingStyle=s.CommonPopupDataAttributes.startingStyle]="startingStyle",r[r.endingStyle=s.CommonPopupDataAttributes.endingStyle]="endingStyle",r.nested="data-nested",r.nestedDialogOpen="data-nested-dialog-open",r);var x=e.i(733332);let C=a.createContext(void 0);function y(){let e=a.useContext(C);if(void 0===e)throw Error((0,x.default)(26));return e}e.s(["DialogPortalContext",0,C,"useDialogPortalContext",0,y],625834);var w=e.i(137584),S=e.i(673327),k=e.i(264111),N=e.i(843476);let M={...s.popupStateMapping,...i.transitionStatusMapping,nestedDialogOpen:e=>e?{[v.nestedDialogOpen]:""}:null},D=a.forwardRef(function(e,t){let{render:r,className:a,style:s,finalFocus:i,initialFocus:l,...d}=e,{store:u}=(0,o.useDialogRootContext)(),c=u.useState("descriptionElementId"),m=u.useState("disablePointerDismissal"),p=u.useState("floatingRootContext"),f=u.useState("popupProps"),g=u.useState("modal"),v=u.useState("mounted"),x=u.useState("nested"),C=u.useState("nestedOpenDialogCount"),D=u.useState("open"),O=u.useState("openMethod"),E=u.useState("titleElementId"),R=u.useState("transitionStatus"),P=u.useState("role"),T=p.useState("floatingId"),j=d.id??T;y(),(0,w.useOpenChangeComplete)({open:D,ref:u.context.popupRef,onComplete(){D&&u.context.onOpenChangeComplete?.(!0)}});let I=void 0===l?(0,k.createDefaultInitialFocus)(u.context.popupRef):l,_=u.useStateSetter("popupElement"),$=(0,n.useRenderElement)("div",e,{state:{open:D,nested:x,transitionStatus:R,nestedDialogOpen:C>0},props:[f,{id:j,"aria-labelledby":E??void 0,"aria-describedby":c??void 0,role:P,...k.FOCUSABLE_POPUP_PROPS,hidden:!v,onKeyDown(e){S.COMPOSITE_KEYS.has(e.key)&&e.stopPropagation()},style:{[b.nestedDialogs]:C}},d],ref:[t,u.context.popupRef,_],stateAttributesMapping:M});return(0,N.jsx)(h.FloatingFocusManager,{context:p,openInteractionType:O,disabled:!v,closeOnFocusOut:!m,initialFocus:I,returnFocus:i,modal:!1!==g,restoreFocus:"popup",children:$})});e.s(["DialogPopup",0,D],784324);var O=e.i(144394),E=e.i(726674),R=e.i(426);let P=a.forwardRef(function(e,t){let{keepMounted:r=!1,...a}=e,{store:n}=(0,o.useDialogRootContext)(),s=n.useState("mounted"),i=n.useState("modal"),l=n.useState("open");return s||r?(0,N.jsx)(C.Provider,{value:r,children:(0,N.jsxs)(E.FloatingPortal,{ref:t,...a,children:[s&&!0===i&&(0,N.jsx)(R.InternalBackdrop,{ref:n.context.internalBackdropRef,inert:(0,O.inertValue)(!l)}),e.children]})}):null});e.s(["DialogPortal",0,P],264951)},67530,e=>{"use strict";var t=e.i(271645),r=e.i(145484),a=e.i(956789),o=e.i(17989),n=e.i(647554),s=e.i(675606),i=e.i(56434),l=e.i(264111);e.s(["DialogInteractions",0,function({store:e,parentContext:s,isDrawer:i}){let d=e.useState("open"),u=e.useState("disablePointerDismissal"),c=e.useState("modal"),m=e.useState("popupElement"),p=e.useState("floatingRootContext"),[f,g]=t.useState(0),[h,b]=t.useState(0),v=0===f,x=(0,o.useDismiss)(p,{outsidePressEvent:()=>e.context.internalBackdropRef.current||e.context.backdropRef.current?"intentional":{mouse:"trap-focus"===c?"sloppy":"intentional",touch:"sloppy"},outsidePress(t){if(!e.context.outsidePressEnabledRef.current||"button"in t&&0!==t.button||"touches"in t&&1!==t.touches.length)return!1;let r=(0,n.getTarget)(t);return!!v&&!u&&(!c||!e.context.internalBackdropRef.current&&!e.context.backdropRef.current||e.context.internalBackdropRef.current===r||e.context.backdropRef.current===r||(0,n.contains)(r,m)&&!r?.hasAttribute("data-base-ui-portal"))},escapeKey:v});(0,r.useScrollLock)(d&&!0===c,m),e.useContextCallback("onNestedDialogOpen",(e,t)=>{g(e),b(t)}),e.useContextCallback("onNestedDialogClose",()=>{g(0),b(0)}),t.useEffect(()=>(s?.onNestedDialogOpen&&d&&s.onNestedDialogOpen(f+1,h+ +!!i),s?.onNestedDialogClose&&!d&&s.onNestedDialogClose(),()=>{s?.onNestedDialogClose&&d&&s.onNestedDialogClose()}),[i,d,f,h,s]);let C=x.reference??a.EMPTY_OBJECT,y=x.trigger??a.EMPTY_OBJECT,w=x.floating??a.EMPTY_OBJECT;return(0,l.usePopupInteractionProps)(e,{activeTriggerProps:C,inactiveTriggerProps:y,popupProps:w,nestedOpenDialogCount:f,nestedOpenDrawerCount:h}),null},"useDialogRoot",0,function(e){let{store:r,actionsRef:a}=e,o=r.useState("open");(0,l.usePopupRootSync)(r,o),(0,l.useImplicitActiveTrigger)(r);let{forceUnmount:n}=(0,l.useOpenStateTransitions)(o,r),d=t.useCallback(()=>{r.setOpen(!1,(0,s.createChangeEventDetails)(i.REASONS.imperativeAction))},[r]);t.useImperativeHandle(a,()=>({unmount:n,close:d}),[n,d])}])},366250,301807,e=>{"use strict";var t=e.i(271645),r=e.i(713203),a=e.i(67530),o=e.i(108821),n=e.i(616269),s=e.i(301252),i=e.i(116786),l=e.i(990627),d=e.i(264111);let u={...i.popupStoreSelectors,modal:(0,n.createSelector)(e=>e.modal),nested:(0,n.createSelector)(e=>e.nested),nestedOpenDialogCount:(0,n.createSelector)(e=>e.nestedOpenDialogCount),nestedOpenDrawerCount:(0,n.createSelector)(e=>e.nestedOpenDrawerCount),disablePointerDismissal:(0,n.createSelector)(e=>e.disablePointerDismissal),openMethod:(0,n.createSelector)(e=>e.openMethod),descriptionElementId:(0,n.createSelector)(e=>e.descriptionElementId),titleElementId:(0,n.createSelector)(e=>e.titleElementId),viewportElement:(0,n.createSelector)(e=>e.viewportElement),role:(0,n.createSelector)(e=>e.role)};class c extends s.ReactStore{constructor(e,r,a=!1){const o=new l.PopupTriggerMap,n=function(e={}){return{...(0,i.createInitialPopupStoreState)(),modal:!0,disablePointerDismissal:!1,popupElement:null,viewportElement:null,descriptionElementId:void 0,titleElementId:void 0,openMethod:null,nested:!1,nestedOpenDialogCount:0,nestedOpenDrawerCount:0,role:"dialog",...e}}(e);n.floatingRootContext=(0,i.createPopupFloatingRootContext)(o,r,a),super(n,{popupRef:t.createRef(),backdropRef:t.createRef(),internalBackdropRef:t.createRef(),outsidePressEnabledRef:{current:!0},triggerElements:o,onOpenChange:void 0,onOpenChangeComplete:void 0},u)}setOpen=(e,t)=>{if(t.preventUnmountOnClose=()=>{this.set("preventUnmountingOnClose",!0)},e||null!=t.trigger||null==this.state.activeTriggerId||(t.trigger=this.state.activeTriggerElement??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let r={open:e};(0,d.setPopupOpenState)(r,e,t.trigger),this.update(r)};static useStore(e,t){return(0,d.usePopupStore)(e,(e,r)=>new c(t,e,r),!0).store}}e.s(["DialogStore",0,c],301807);var m=e.i(843476);e.s(["useRenderDialogRoot",0,function(e,n="dialog"){let{children:s,open:i,defaultOpen:l=!1,onOpenChange:d,onOpenChangeComplete:u,disablePointerDismissal:p=!1,modal:f=!0,actionsRef:g,handle:h,triggerId:b,defaultTriggerId:v=null}=e,x="alert-dialog"===n,C=(0,o.useDialogRootContext)(!0),y={modal:!!x||f,disablePointerDismissal:x||p,nested:!!C,role:x?"alertdialog":"dialog"},w=c.useStore(h?.store,{open:l,openProp:i,activeTriggerId:v,triggerIdProp:b,...y});(0,r.useOnFirstRender)(()=>{let e=void 0===i&&!1===w.state.open&&!0===l?{open:!0,activeTriggerId:v}:null;x?w.update(e?{...y,...e}:y):e&&w.update(e)}),w.useControlledProp("openProp",i),w.useControlledProp("triggerIdProp",b),w.useSyncedValues(y),w.useContextCallback("onOpenChange",d),w.useContextCallback("onOpenChangeComplete",u);let S=w.useState("open"),k=w.useState("mounted"),N=w.useState("payload");(0,a.useDialogRoot)({store:w,actionsRef:g});let M=t.useMemo(()=>({store:w}),[w]);return(0,m.jsx)(o.IsDrawerContext.Provider,{value:!1,children:(0,m.jsxs)(o.DialogRootContext.Provider,{value:M,children:[(S||k)&&(0,m.jsx)(a.DialogInteractions,{store:w,parentContext:C?.store.context,isDrawer:"drawer"===n}),"function"==typeof s?s({payload:N}):s]})})}],366250)},974217,e=>{"use strict";e.i(247167);var t,r=e.i(271645),a=e.i(552245),o=e.i(405005),n=e.i(209407),s=e.i(108821),i=e.i(625834);let l=((t={})[t.open=o.CommonPopupDataAttributes.open]="open",t[t.closed=o.CommonPopupDataAttributes.closed]="closed",t[t.startingStyle=o.CommonPopupDataAttributes.startingStyle]="startingStyle",t[t.endingStyle=o.CommonPopupDataAttributes.endingStyle]="endingStyle",t.nested="data-nested",t.nestedDialogOpen="data-nested-dialog-open",t),d={...o.popupStateMapping,...n.transitionStatusMapping,nested:e=>e?{[l.nested]:""}:null,nestedDialogOpen:e=>e?{[l.nestedDialogOpen]:""}:null},u=r.forwardRef(function(e,t){let{render:r,className:o,style:n,children:l,...u}=e,c=(0,i.useDialogPortalContext)(),{store:m}=(0,s.useDialogRootContext)(),p=m.useState("open"),f=m.useState("nested"),g=m.useState("transitionStatus"),h=m.useState("nestedOpenDialogCount"),b=m.useState("mounted"),v=m.useStateSetter("viewportElement");return(0,a.useRenderElement)("div",e,{enabled:c||b,state:{open:p,nested:f,transitionStatus:g,nestedDialogOpen:h>0},ref:[t,v],stateAttributesMapping:d,props:[{role:"presentation",hidden:!b,style:{pointerEvents:p?void 0:"none"},children:l},u]})});e.s(["DialogViewport",0,u],974217)},77173,313488,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(108821),a=e.i(552245),o=e.i(788015);let n=t.forwardRef(function(e,t){let{render:n,className:s,style:i,id:l,...d}=e,{store:u}=(0,r.useDialogRootContext)(),c=(0,o.useBaseUiId)(l);return u.useSyncedValueWithCleanup("titleElementId",c),(0,a.useRenderElement)("h2",e,{ref:t,props:[{id:c},d]})});e.s(["DialogTitle",0,n],77173);var s=e.i(733332),i=e.i(540886),l=e.i(405005),d=e.i(638396),u=e.i(264111),c=e.i(385689),m=e.i(32199);let p=t.forwardRef(function(e,n){let{render:p,className:f,style:g,disabled:h=!1,nativeButton:b=!0,id:v,payload:x,handle:C,...y}=e,w=(0,r.useDialogRootContext)(!0),S=C?.store??w?.store;if(!S)throw Error((0,s.default)(79));let k=(0,o.useBaseUiId)(v),N=S.useState("floatingRootContext"),M=S.useState("isOpenedByTrigger",k),D=S.useState("triggerPopupId",k),O=t.useRef(null),{registerTrigger:E,isMountedByThisTrigger:R}=(0,u.useTriggerDataForwarding)(k,O,S,{payload:x}),{getButtonProps:P,buttonRef:T}=(0,i.useButton)({disabled:h,native:b}),j=(0,c.useClick)(N,{enabled:null!=N}),I=(0,m.useOpenMethodTriggerProps)(()=>S.select("open"),e=>{S.set("openMethod",e)}),_=S.useState("triggerProps",R);return(0,a.useRenderElement)("button",e,{state:{disabled:h,open:M},ref:[T,n,E,O],props:[j.reference,_,I,{[d.CLICK_TRIGGER_IDENTIFIER]:"",id:k,"aria-haspopup":"dialog","aria-expanded":M,"aria-controls":D},y,P],stateAttributesMapping:l.triggerOpenStateMapping})});e.s(["DialogTrigger",0,p],313488)},325326,e=>{"use strict";var t=e.i(301807),r=e.i(675606),a=e.i(56434);class o{constructor(e){this.store=e??new t.DialogStore}open(e){let t=e?this.store.context.triggerElements.getById(e):void 0;this.store.setOpen(!0,(0,r.createChangeEventDetails)(a.REASONS.imperativeAction,void 0,t))}openWithPayload(e){this.store.set("payload",e),this.store.setOpen(!0,(0,r.createChangeEventDetails)(a.REASONS.imperativeAction,void 0,void 0))}close(){this.store.setOpen(!1,(0,r.createChangeEventDetails)(a.REASONS.imperativeAction,void 0,void 0))}get isOpen(){return this.store.select("open")}}e.s(["DialogHandle",0,o,"createDialogHandle",0,function(){return new o}])},353753,e=>{"use strict";e.s([],914651),e.i(914651);var t=e.i(402820),r=e.i(156736),a=e.i(209793),o=e.i(784324),n=e.i(264951),s=e.i(271645),i=e.i(108821),l=e.i(366250),d=e.i(974217),u=e.i(77173),c=e.i(313488),m=e.i(325326);e.s(["Backdrop",()=>t.DialogBackdrop,"Close",()=>r.DialogClose,"Description",()=>a.DialogDescription,"Handle",()=>m.DialogHandle,"Popup",()=>o.DialogPopup,"Portal",()=>n.DialogPortal,"Root",0,function(e){let t=s.useContext(i.IsDrawerContext)?"drawer":"dialog";return(0,l.useRenderDialogRoot)(e,t)},"Title",()=>u.DialogTitle,"Trigger",()=>c.DialogTrigger,"Viewport",()=>d.DialogViewport,"createHandle",()=>m.createDialogHandle],828376);var p=e.i(828376);e.s(["Dialog",0,p],353753)},793479,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,type:r,...o},n)=>(0,t.jsx)("input",{type:r,"data-slot":"input",className:(0,a.cn)("h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30","focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50","aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",e),ref:n,...o}));o.displayName="Input",e.s(["Input",0,o])},172410,e=>{"use strict";var t=e.i(271645);let r=t.createContext(void 0),a={disableStyleElements:!1};e.s(["useCSPContext",0,function(){return t.useContext(r)??a}])},550896,e=>{"use strict";var t=e.i(201675);e.s(["SCROLL_EDGE_TOLERANCE_PX",0,1,"getMaxScrollOffset",0,function(e,t){return Math.max(0,e-t)},"normalizeScrollOffset",0,function(e,r){if(r<=0)return 0;let a=(0,t.clamp)(e,0,r),o=r-a,n=a<=1,s=o<=1;return n&&s?a<=o?0:r:n?0:s?r:a}])},60837,e=>{"use strict";var t=e.i(843476);let r="base-ui-disable-scrollbar";e.s(["styleDisableScrollbar",0,{className:r,getElement:e=>(0,t.jsx)("style",{nonce:e,href:r,precedence:"base-ui:low",children:`.${r}{scrollbar-width:none}.${r}::-webkit-scrollbar{display:none}`})}])},822315,(e,t,r)=>{e.e,t.exports=function(){"use strict";var e="millisecond",t="second",r="minute",a="hour",o="week",n="month",s="quarter",i="year",l="date",d="Invalid Date",u=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,c=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,m=function(e,t,r){var a=String(e);return!a||a.length>=t?e:""+Array(t+1-a.length).join(r)+e},p="en",f={};f[p]={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(e){var t=["th","st","nd","rd"],r=e%100;return"["+e+(t[(r-20)%10]||t[r]||t[0])+"]"}};var g="$isDayjsObject",h=function(e){return e instanceof C||!(!e||!e[g])},b=function e(t,r,a){var o;if(!t)return p;if("string"==typeof t){var n=t.toLowerCase();f[n]&&(o=n),r&&(f[n]=r,o=n);var s=t.split("-");if(!o&&s.length>1)return e(s[0])}else{var i=t.name;f[i]=t,o=i}return!a&&o&&(p=o),o||!a&&p},v=function(e,t){if(h(e))return e.clone();var r="object"==typeof t?t:{};return r.date=e,r.args=arguments,new C(r)},x={s:m,z:function(e){var t=-e.utcOffset(),r=Math.abs(t);return(t<=0?"+":"-")+m(Math.floor(r/60),2,"0")+":"+m(r%60,2,"0")},m:function e(t,r){if(t.date(){"use strict";var t=e.i(309821);e.s(["Progress",()=>t.default])},438100,e=>{"use strict";let t=(0,e.i(475254).default)("key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);e.s(["KeyIcon",0,t],438100)},988846,e=>{"use strict";var t=e.i(54943);e.s(["SearchIcon",()=>t.default])},95684,e=>{"use strict";var t=e.i(165370);e.s(["Pagination",()=>t.default])},372943,897565,166452,e=>{"use strict";e.i(247167);var t=e.i(8211),r=e.i(271645),a=e.i(343794),o=e.i(529681),n=e.i(242064),s=e.i(704914),i=e.i(876556),l=e.i(290224),d=e.i(251224),u=function(e,t){var r={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,a=Object.getOwnPropertySymbols(e);ot.indexOf(a[o])&&Object.prototype.propertyIsEnumerable.call(e,a[o])&&(r[a[o]]=e[a[o]]);return r};function c({suffixCls:e,tagName:t,displayName:a}){return a=>r.forwardRef((o,n)=>r.createElement(a,Object.assign({ref:n,suffixCls:e,tagName:t},o)))}let m=r.forwardRef((e,t)=>{let{prefixCls:o,suffixCls:s,className:i,tagName:l}=e,c=u(e,["prefixCls","suffixCls","className","tagName"]),{getPrefixCls:m}=r.useContext(n.ConfigContext),p=m("layout",o),[f,g,h]=(0,d.default)(p),b=s?`${p}-${s}`:p;return f(r.createElement(l,Object.assign({className:(0,a.default)(o||b,i,g,h),ref:t},c)))}),p=r.forwardRef((e,c)=>{let{direction:m}=r.useContext(n.ConfigContext),[p,f]=r.useState([]),{prefixCls:g,className:h,rootClassName:b,children:v,hasSider:x,tagName:C,style:y}=e,w=u(e,["prefixCls","className","rootClassName","children","hasSider","tagName","style"]),S=(0,o.default)(w,["suffixCls"]),{getPrefixCls:k,className:N,style:M}=(0,n.useComponentConfig)("layout"),D=k("layout",g),O="boolean"==typeof x?x:!!p.length||(0,i.default)(v).some(e=>e.type===l.default),[E,R,P]=(0,d.default)(D),T=(0,a.default)(D,{[`${D}-has-sider`]:O,[`${D}-rtl`]:"rtl"===m},N,h,b,R,P),j=r.useMemo(()=>({siderHook:{addSider:e=>{f(r=>[].concat((0,t.default)(r),[e]))},removeSider:e=>{f(t=>t.filter(t=>t!==e))}}}),[]);return E(r.createElement(s.LayoutContext.Provider,{value:j},r.createElement(C,Object.assign({ref:c,className:T,style:Object.assign(Object.assign({},M),y)},S),v)))}),f=c({tagName:"div",displayName:"Layout"})(p),g=c({suffixCls:"header",tagName:"header",displayName:"Header"})(m),h=c({suffixCls:"footer",tagName:"footer",displayName:"Footer"})(m),b=c({suffixCls:"content",tagName:"main",displayName:"Content"})(m);f.Header=g,f.Footer=h,f.Content=b,f.Sider=l.default,f._InternalSiderContext=l.SiderContext,e.s(["Layout",0,f],372943);let v=(0,e.i(475254).default)("layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);e.s(["LayersIcon",0,v],897565);var x=e.i(98740);e.s(["UsersIcon",()=>x.default],166452)},160818,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.4 800.9c.2-.3.5-.6.7-.9C920.6 722.1 960 621.7 960 512s-39.4-210.1-104.8-288c-.2-.3-.5-.5-.7-.8-1.1-1.3-2.1-2.5-3.2-3.7-.4-.5-.8-.9-1.2-1.4l-4.1-4.7-.1-.1c-1.5-1.7-3.1-3.4-4.6-5.1l-.1-.1c-3.2-3.4-6.4-6.8-9.7-10.1l-.1-.1-4.8-4.8-.3-.3c-1.5-1.5-3-2.9-4.5-4.3-.5-.5-1-1-1.6-1.5-1-1-2-1.9-3-2.8-.3-.3-.7-.6-1-1C736.4 109.2 629.5 64 512 64s-224.4 45.2-304.3 119.2c-.3.3-.7.6-1 1-1 .9-2 1.9-3 2.9-.5.5-1 1-1.6 1.5-1.5 1.4-3 2.9-4.5 4.3l-.3.3-4.8 4.8-.1.1c-3.3 3.3-6.5 6.7-9.7 10.1l-.1.1c-1.6 1.7-3.1 3.4-4.6 5.1l-.1.1c-1.4 1.5-2.8 3.1-4.1 4.7-.4.5-.8.9-1.2 1.4-1.1 1.2-2.1 2.5-3.2 3.7-.2.3-.5.5-.7.8C103.4 301.9 64 402.3 64 512s39.4 210.1 104.8 288c.2.3.5.6.7.9l3.1 3.7c.4.5.8.9 1.2 1.4l4.1 4.7c0 .1.1.1.1.2 1.5 1.7 3 3.4 4.6 5l.1.1c3.2 3.4 6.4 6.8 9.6 10.1l.1.1c1.6 1.6 3.1 3.2 4.7 4.7l.3.3c3.3 3.3 6.7 6.5 10.1 9.6 80.1 74 187 119.2 304.5 119.2s224.4-45.2 304.3-119.2a300 300 0 0010-9.6l.3-.3c1.6-1.6 3.2-3.1 4.7-4.7l.1-.1c3.3-3.3 6.5-6.7 9.6-10.1l.1-.1c1.5-1.7 3.1-3.3 4.6-5 0-.1.1-.1.1-.2 1.4-1.5 2.8-3.1 4.1-4.7.4-.5.8-.9 1.2-1.4a99 99 0 003.3-3.7zm4.1-142.6c-13.8 32.6-32 62.8-54.2 90.2a444.07 444.07 0 00-81.5-55.9c11.6-46.9 18.8-98.4 20.7-152.6H887c-3 40.9-12.6 80.6-28.5 118.3zM887 484H743.5c-1.9-54.2-9.1-105.7-20.7-152.6 29.3-15.6 56.6-34.4 81.5-55.9A373.86 373.86 0 01887 484zM658.3 165.5c39.7 16.8 75.8 40 107.6 69.2a394.72 394.72 0 01-59.4 41.8c-15.7-45-35.8-84.1-59.2-115.4 3.7 1.4 7.4 2.9 11 4.4zm-90.6 700.6c-9.2 7.2-18.4 12.7-27.7 16.4V697a389.1 389.1 0 01115.7 26.2c-8.3 24.6-17.9 47.3-29 67.8-17.4 32.4-37.8 58.3-59 75.1zm59-633.1c11 20.6 20.7 43.3 29 67.8A389.1 389.1 0 01540 327V141.6c9.2 3.7 18.5 9.1 27.7 16.4 21.2 16.7 41.6 42.6 59 75zM540 640.9V540h147.5c-1.6 44.2-7.1 87.1-16.3 127.8l-.3 1.2A445.02 445.02 0 00540 640.9zm0-156.9V383.1c45.8-2.8 89.8-12.5 130.9-28.1l.3 1.2c9.2 40.7 14.7 83.5 16.3 127.8H540zm-56 56v100.9c-45.8 2.8-89.8 12.5-130.9 28.1l-.3-1.2c-9.2-40.7-14.7-83.5-16.3-127.8H484zm-147.5-56c1.6-44.2 7.1-87.1 16.3-127.8l.3-1.2c41.1 15.6 85 25.3 130.9 28.1V484H336.5zM484 697v185.4c-9.2-3.7-18.5-9.1-27.7-16.4-21.2-16.7-41.7-42.7-59.1-75.1-11-20.6-20.7-43.3-29-67.8 37.2-14.6 75.9-23.3 115.8-26.1zm0-370a389.1 389.1 0 01-115.7-26.2c8.3-24.6 17.9-47.3 29-67.8 17.4-32.4 37.8-58.4 59.1-75.1 9.2-7.2 18.4-12.7 27.7-16.4V327zM365.7 165.5c3.7-1.5 7.3-3 11-4.4-23.4 31.3-43.5 70.4-59.2 115.4-21-12-40.9-26-59.4-41.8 31.8-29.2 67.9-52.4 107.6-69.2zM165.5 365.7c13.8-32.6 32-62.8 54.2-90.2 24.9 21.5 52.2 40.3 81.5 55.9-11.6 46.9-18.8 98.4-20.7 152.6H137c3-40.9 12.6-80.6 28.5-118.3zM137 540h143.5c1.9 54.2 9.1 105.7 20.7 152.6a444.07 444.07 0 00-81.5 55.9A373.86 373.86 0 01137 540zm228.7 318.5c-39.7-16.8-75.8-40-107.6-69.2 18.5-15.8 38.4-29.7 59.4-41.8 15.7 45 35.8 84.1 59.2 115.4-3.7-1.4-7.4-2.9-11-4.4zm292.6 0c-3.7 1.5-7.3 3-11 4.4 23.4-31.3 43.5-70.4 59.2-115.4 21 12 40.9 26 59.4 41.8a373.81 373.81 0 01-107.6 69.2z"}}]},name:"global",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["GlobalOutlined",0,n],160818)},784774,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("div",{"data-slot":"table-container",className:"relative w-full overflow-x-auto",children:(0,t.jsx)("table",{ref:o,"data-slot":"table",className:(0,a.cn)("w-full caption-bottom text-sm",e),...r})}));o.displayName="Table";let n=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("thead",{ref:o,"data-slot":"table-header",className:(0,a.cn)("[&_tr]:border-b",e),...r}));n.displayName="TableHeader";let s=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("tbody",{ref:o,"data-slot":"table-body",className:(0,a.cn)("[&_tr:last-child]:border-0",e),...r}));s.displayName="TableBody";let i=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("tfoot",{ref:o,"data-slot":"table-footer",className:(0,a.cn)("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",e),...r}));i.displayName="TableFooter";let l=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("tr",{ref:o,"data-slot":"table-row",className:(0,a.cn)("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",e),...r}));l.displayName="TableRow";let d=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("th",{ref:o,"data-slot":"table-head",className:(0,a.cn)("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...r}));d.displayName="TableHead";let u=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("td",{ref:o,"data-slot":"table-cell",className:(0,a.cn)("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...r}));u.displayName="TableCell",r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("caption",{ref:o,"data-slot":"table-caption",className:(0,a.cn)("mt-4 text-sm text-muted-foreground",e),...r})).displayName="TableCaption",e.s(["Table",0,o,"TableBody",0,s,"TableCell",0,u,"TableFooter",0,i,"TableHead",0,d,"TableHeader",0,n,"TableRow",0,l])},302747,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("div",{ref:o,"data-slot":"skeleton",className:(0,a.cn)("animate-pulse rounded-md bg-accent",e),...r}));o.displayName="Skeleton",e.s(["Skeleton",0,o])},110204,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("label",{ref:o,"data-slot":"label",className:(0,a.cn)("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",e),...r}));o.displayName="Label",e.s(["Label",0,o])},16715,e=>{"use strict";let t=(0,e.i(475254).default)("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);e.s(["RefreshCw",0,t],16715)},590803,e=>{"use strict";e.s(["isElementDisabled",0,function(e){return null==e||e.hasAttribute("disabled")||"true"===e.getAttribute("aria-disabled")}])},53687,673553,395530,e=>{"use strict";var t,r=e.i(271645),a=e.i(921374),o=e.i(667865),n=e.i(146376);e.i(247167);let s=r.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},elementsRef:{current:[]},nextIndexRef:{current:0}});var i=e.i(843476);function l(){return new Map}function d(){return new Set}function u(e,t){let r=e.compareDocumentPosition(t);return r&Node.DOCUMENT_POSITION_FOLLOWING||r&Node.DOCUMENT_POSITION_CONTAINED_BY?-1:r&Node.DOCUMENT_POSITION_PRECEDING||r&Node.DOCUMENT_POSITION_CONTAINS?1:0}e.s(["CompositeList",0,function(e){let{children:t,elementsRef:c,labelsRef:m,onMapChange:p}=e,f=(0,o.useStableCallback)(p),g=r.useRef(0),h=(0,a.useRefWithInit)(d).current,b=(0,a.useRefWithInit)(l).current,[v,x]=r.useState(0),C=r.useRef(v),y=(0,o.useStableCallback)((e,t)=>{b.set(e,t??null),C.current+=1,x(C.current)}),w=(0,o.useStableCallback)(e=>{b.delete(e),C.current+=1,x(C.current)}),S=r.useMemo(()=>{let e=new Map;return Array.from(b.keys()).filter(e=>e.isConnected).sort(u).forEach((t,r)=>{let a=b.get(t)??{};e.set(t,{...a,index:r})}),e},[b,v]);(0,n.useIsoLayoutEffect)(()=>{if("function"!=typeof MutationObserver||0===S.size)return;let e=new MutationObserver(e=>{let t=new Set,r=e=>t.has(e)?t.delete(e):t.add(e);e.forEach(e=>{e.removedNodes.forEach(r),e.addedNodes.forEach(r)}),0===t.size&&(C.current+=1,x(C.current))});return S.forEach((t,r)=>{r.parentElement&&e.observe(r.parentElement,{childList:!0})}),()=>{e.disconnect()}},[S]),(0,n.useIsoLayoutEffect)(()=>{C.current===v&&(c.current.length!==S.size&&(c.current.length=S.size),m&&m.current.length!==S.size&&(m.current.length=S.size),g.current=S.size),f(S)},[f,S,c,m,v]),(0,n.useIsoLayoutEffect)(()=>()=>{c.current=[]},[c]),(0,n.useIsoLayoutEffect)(()=>()=>{m&&(m.current=[])},[m]);let k=(0,o.useStableCallback)(e=>(h.add(e),()=>{h.delete(e)}));(0,n.useIsoLayoutEffect)(()=>{h.forEach(e=>e(S))},[h,S]);let N=r.useMemo(()=>({register:y,unregister:w,subscribeMapChange:k,elementsRef:c,labelsRef:m,nextIndexRef:g}),[y,w,k,c,m,g]);return(0,i.jsx)(s.Provider,{value:N,children:t})}],53687);var c=e.i(828918),m=e.i(838452);let p=((t={})[t.None=0]="None",t[t.GuessFromOrder=1]="GuessFromOrder",t);function f(e={}){let{label:t,metadata:a,textRef:o,indexGuessBehavior:i,index:l}=e,{register:d,unregister:u,subscribeMapChange:c,elementsRef:m,labelsRef:g,nextIndexRef:h}=r.useContext(s),b=r.useRef(-1),[v,x]=r.useState(l??(i===p.GuessFromOrder?()=>{if(-1===b.current){let e=h.current;h.current+=1,b.current=e}return b.current}:-1)),C=r.useRef(null),y=r.useCallback(e=>{if(C.current=e,-1!==v&&null!==e&&(m.current[v]=e,g)){let r=void 0!==t;g.current[v]=r?t:o?.current?.textContent??e.textContent}},[v,m,g,t,o]);return(0,n.useIsoLayoutEffect)(()=>{if(null!=l)return;let e=C.current;if(e)return d(e,a),()=>{u(e)}},[l,d,u,a]),(0,n.useIsoLayoutEffect)(()=>{if(null==l)return c(e=>{let t=C.current?e.get(C.current)?.index:null;null!=t&&x(t)})},[l,c,x]),{ref:y,index:v}}e.s(["IndexGuessBehavior",0,p,"useCompositeListItem",0,f],673553),e.s(["useCompositeItem",0,function(e={}){let{highlightItemOnHover:t,highlightedIndex:a,onHighlightedIndexChange:o}=(0,m.useCompositeRootContext)(),{ref:n,index:s}=f(e),i=a===s,l=r.useRef(null),d=(0,c.useMergedRefs)(n,l);return{compositeProps:{tabIndex:i?0:-1,onFocus(){o(s)},onMouseMove(){let e=l.current;if(!t||!e)return;let r=e.hasAttribute("disabled")||"true"===e.ariaDisabled;i||r||e.focus()}},compositeRef:d,index:s}}],395530)},625901,e=>{"use strict";var t=e.i(266027),r=e.i(621482),a=e.i(243652),o=e.i(602869),n=e.i(135214);let s=(0,a.createQueryKeys)("models"),i=(0,a.createQueryKeys)("modelHub"),l=(0,a.createQueryKeys)("allProxyModels");(0,a.createQueryKeys)("selectedTeamModels");let d=(0,a.createQueryKeys)("infiniteModels"),u=(0,a.createQueryKeys)("userModels");e.s(["useAllProxyModels",0,()=>{let{accessToken:e,userId:r,userRole:a}=(0,n.default)();return(0,t.useQuery)({queryKey:l.list({}),queryFn:async()=>await (0,o.modelAvailableCall)(e,r,a,!0,null,!0,!1,"expand"),enabled:!!(e&&r&&a)})},"useInfiniteModelInfo",0,(e=50,t)=>{let{accessToken:a,userId:s,userRole:i}=(0,n.default)();return(0,r.useInfiniteQuery)({queryKey:d.list({filters:{...s&&{userId:s},...i&&{userRole:i},size:e,...t&&{search:t}}}),queryFn:async({pageParam:r})=>await (0,o.modelInfoCall)(a,s,i,r,e,t),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{let{accessToken:e}=(0,n.default)();return(0,t.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,o.modelHubCall)(e),enabled:!!e})},"useModelsInfo",0,(e=1,r=50,a,i,l,d,u)=>{let{accessToken:c,userId:m,userRole:p}=(0,n.default)();return(0,t.useQuery)({queryKey:s.list({filters:{...m&&{userId:m},...p&&{userRole:p},page:e,size:r,...a&&{search:a},...i&&{modelId:i},...l&&{teamId:l},...d&&{sortBy:d},...u&&{sortOrder:u}}}),queryFn:async()=>await (0,o.modelInfoCall)(c,m,p,e,r,a,i,l,d,u),enabled:!!(c&&m&&p)})},"useUserModels",0,()=>{let{accessToken:e,userId:r,userRole:a}=(0,n.default)();return(0,t.useQuery)({queryKey:u.list({}),queryFn:async()=>(await (0,o.modelAvailableCall)(e,r,a)).data.map(e=>e.id),enabled:!!(e&&r&&a)})}])},738014,e=>{"use strict";var t=e.i(135214),r=e.i(602869),a=e.i(266027);let o=(0,e.i(243652).createQueryKeys)("users");e.s(["useCurrentUser",0,()=>{let{accessToken:e,userId:n}=(0,t.default)();return(0,a.useQuery)({queryKey:o.detail(n),queryFn:async()=>await (0,r.userGetInfoV2)(e),enabled:!!(e&&n)})}])},162386,e=>{"use strict";var t=e.i(843476),r=e.i(625901),a=e.i(109799),o=e.i(785242),n=e.i(738014),s=e.i(199133),i=e.i(981339),l=e.i(592968);let d={label:"All Proxy Models",value:"all-proxy-models"},u={label:"No Default Models",value:"no-default-models"},c=[d,u],m={user:({allProxyModels:e,userModels:t,options:r})=>t&&r?.includeUserModels?t:[],team:({allProxyModels:e,selectedOrganization:t,userModels:r})=>t?t.models.includes(d.value)||0===t.models.length?e:e.filter(e=>t.models.includes(e)):e??[],organization:({allProxyModels:e})=>e,global:({allProxyModels:e})=>e};e.s(["ModelSelect",0,e=>{let{teamID:p,organizationID:f,options:g,context:h,dataTestId:b,value:v=[],onChange:x,style:C}=e,{includeUserModels:y,showAllTeamModelsOption:w,showAllProxyModelsOverride:S,includeSpecialOptions:k}=g||{},{data:N,isLoading:M}=(0,r.useAllProxyModels)(),{data:D,isLoading:O}=(0,o.useTeam)(p),{data:E,isLoading:R}=(0,a.useOrganization)(f),{data:P,isLoading:T}=(0,n.useCurrentUser)(),j=e=>c.some(t=>t.value===e),I=v.some(j),_=E?.models.includes(d.value)||E?.models.length===0;if(M||O||R||T)return(0,t.jsx)(i.Skeleton.Input,{active:!0,block:!0});let{wildcard:$,regular:B}=(e=>{let t=[],r=[];for(let a of e)a.endsWith("/*")?t.push(a):r.push(a);return{wildcard:t,regular:r}})(((e,t,r)=>{let a=Array.from(new Map(e.map(e=>[e.id,e])).values()).map(e=>e.id);if(t.options?.showAllProxyModelsOverride)return a;let o=m[t.context];return o?o({allProxyModels:a,...r,options:t.options}):[]})(N?.data??[],e,{selectedTeam:D,selectedOrganization:E,userModels:P?.models}));return(0,t.jsx)(s.Select,{"data-testid":b,value:v,onChange:e=>{let t=e.filter(j);x(t.length>0?[t[t.length-1]]:e)},style:C,options:[...k?[{label:(0,t.jsx)("span",{children:"Special Options"}),title:"Special Options",options:[...S||_&&k||"global"===h?[{label:(0,t.jsx)("span",{children:"All Proxy Models"}),value:d.value,disabled:v.length>0&&v.some(e=>j(e)&&e!==d.value),key:d.value}]:[],{label:(0,t.jsx)("span",{children:"No Default Models"}),value:u.value,disabled:v.length>0&&v.some(e=>j(e)&&e!==u.value),key:u.value}]}]:[],...$.length>0?[{label:(0,t.jsx)("span",{children:"Wildcard Options"}),title:"Wildcard Options",options:$.map(e=>{let r=e.replace("/*",""),a=r.charAt(0).toUpperCase()+r.slice(1);return{label:(0,t.jsx)("span",{children:`All ${a} models`}),value:e,disabled:I}})}]:[],{label:(0,t.jsx)("span",{children:"Models"}),title:"Models",options:B.map(e=>({label:(0,t.jsx)("span",{children:e}),value:e,disabled:I}))}],mode:"multiple",placeholder:"Select Models",allowClear:!0,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(l.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})})}],162386)},907308,276173,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(212931),o=e.i(808613),n=e.i(464571),s=e.i(199133),i=e.i(592968),l=e.i(213205),d=e.i(374009),u=e.i(602869);e.s(["default",0,({isVisible:e,onCancel:c,onSubmit:m,accessToken:p,title:f="Add Team Member",roles:g=[{label:"admin",value:"admin",description:"Admin role. Can create team keys, add members, and manage settings."},{label:"user",value:"user",description:"User role. Can view team info, but not manage it."}],defaultRole:h="user",teamId:b})=>{let[v]=o.Form.useForm(),[x,C]=(0,r.useState)([]),[y,w]=(0,r.useState)(!1),[S,k]=(0,r.useState)("user_email"),[N,M]=(0,r.useState)(!1),D=async(e,t)=>{if(!e)return void C([]);w(!0);try{let r=new URLSearchParams;if(r.append(t,e),b&&r.append("team_id",b),null==p)return;let a=(await (0,u.userFilterUICall)(p,r)).map(e=>({label:"user_email"===t?`${e.user_email}`:`${e.user_id}`,value:"user_email"===t?e.user_email:e.user_id,user:e}));C(a)}catch(e){console.error("Error fetching users:",e)}finally{w(!1)}},O=(0,r.useCallback)((0,d.default)((e,t)=>D(e,t),300),[]),E=(e,t)=>{k(t),O(e,t)},R=(e,t)=>{let r=t.user;v.setFieldsValue({user_email:r.user_email,user_id:r.user_id,role:v.getFieldValue("role")})},P=async e=>{M(!0);try{await m(e)}finally{M(!1)}};return(0,t.jsx)(a.Modal,{title:f,open:e,onCancel:()=>{v.resetFields(),C([]),c()},footer:null,width:800,maskClosable:!N,children:(0,t.jsxs)(o.Form,{form:v,onFinish:P,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:{role:h},children:[(0,t.jsx)(o.Form.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,t.jsx)(s.Select,{showSearch:!0,className:"w-full",placeholder:"Search by email",filterOption:!1,onSearch:e=>E(e,"user_email"),onSelect:(e,t)=>R(e,t),options:"user_email"===S?x:[],loading:y,allowClear:!0,"data-testid":"member-email-search"})}),(0,t.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,t.jsx)(o.Form.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,t.jsx)(s.Select,{showSearch:!0,className:"w-full",placeholder:"Search by user ID",filterOption:!1,onSearch:e=>E(e,"user_id"),onSelect:(e,t)=>R(e,t),options:"user_id"===S?x:[],loading:y,allowClear:!0})}),(0,t.jsx)(o.Form.Item,{label:"Member Role",name:"role",className:"mb-4",children:(0,t.jsx)(s.Select,{defaultValue:h,children:g.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:(0,t.jsxs)(i.Tooltip,{title:e.description,children:[(0,t.jsx)("span",{className:"font-medium",children:e.label}),(0,t.jsxs)("span",{className:"ml-2 text-gray-500 text-sm",children:["- ",e.description]})]})},e.value))})}),(0,t.jsx)("div",{className:"text-right mt-4",children:(0,t.jsx)(n.Button,{type:"primary",htmlType:"submit",icon:(0,t.jsx)(l.UserAddOutlined,{}),loading:N,children:N?"Adding...":"Add Member"})})]})})}],907308);var c=e.i(599724),m=e.i(779241),p=e.i(435451),f=e.i(860585);e.s(["default",0,({visible:e,onCancel:i,onSubmit:l,initialData:d,mode:u,config:g})=>{let h,[b]=o.Form.useForm(),[v,x]=(0,r.useState)(!1);(0,r.useEffect)(()=>{if(e)if("edit"===u&&d){let e={...d,role:d.role||g.defaultRole,max_budget_in_team:d.max_budget_in_team||null,tpm_limit:d.tpm_limit||null,rpm_limit:d.rpm_limit||null,budget_duration:d.budget_duration||null,allowed_models:d.allowed_models||[]};b.setFieldsValue(e)}else b.resetFields(),b.setFieldsValue({role:g.defaultRole||g.roleOptions[0]?.value})},[e,d,u,b,g.defaultRole,g.roleOptions]);let C=async e=>{try{x(!0);let t=Object.entries(e).reduce((e,[t,r])=>{if("string"==typeof r){let a=r.trim();return""===a&&("max_budget_in_team"===t||"tpm_limit"===t||"rpm_limit"===t)?{...e,[t]:null}:{...e,[t]:a}}return{...e,[t]:r}},{});await Promise.resolve(l(t)),b.resetFields()}catch(e){console.error("Form submission error:",e)}finally{x(!1)}};return(0,t.jsx)(a.Modal,{title:g.title||("add"===u?"Add Member":"Edit Member"),open:e,width:1e3,footer:null,onCancel:i,children:(0,t.jsxs)(o.Form,{form:b,onFinish:C,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[g.showEmail&&(0,t.jsx)(o.Form.Item,{label:"Email",name:"user_email",className:"mb-4",rules:[{type:"email",message:"Please enter a valid email!"}],children:(0,t.jsx)(m.TextInput,{placeholder:"user@example.com"})}),g.showEmail&&g.showUserId&&(0,t.jsx)("div",{className:"text-center mb-4",children:(0,t.jsx)(c.Text,{children:"OR"})}),g.showUserId&&(0,t.jsx)(o.Form.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,t.jsx)(m.TextInput,{placeholder:"user_123"})}),(0,t.jsx)(o.Form.Item,{label:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{children:"Role"}),"edit"===u&&d&&(0,t.jsxs)("span",{className:"text-gray-500 text-sm",children:["(Current: ",(h=d.role,g.roleOptions.find(e=>e.value===h)?.label||h),")"]})]}),name:"role",className:"mb-4",rules:[{required:!0,message:"Please select a role!"}],children:(0,t.jsx)(s.Select,{children:"edit"===u&&d?[...g.roleOptions.filter(e=>e.value===d.role),...g.roleOptions.filter(e=>e.value!==d.role)].map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value)):g.roleOptions.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value))})}),g.additionalFields?.map(e=>(0,t.jsx)(o.Form.Item,{label:e.label,name:e.name,className:"mb-4",rules:e.rules,children:(e=>{switch(e.type){case"input":return(0,t.jsx)(m.TextInput,{placeholder:e.placeholder});case"numerical":return(0,t.jsx)(p.default,{step:e.step||1,min:e.min||0,style:{width:"100%"},placeholder:e.placeholder||"Enter a numerical value"});case"select":return(0,t.jsx)(s.Select,{children:e.options?.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value))});case"multi-select":return(0,t.jsx)(s.Select,{mode:"multiple",placeholder:e.placeholder||"Select options",options:e.options,allowClear:!0});case"budget-duration":return(0,t.jsx)(f.default,{});default:return null}})(e)},e.name)),(0,t.jsxs)("div",{className:"text-right mt-6",children:[(0,t.jsx)(n.Button,{onClick:i,className:"mr-2",disabled:v,children:"Cancel"}),(0,t.jsx)(n.Button,{type:"default",htmlType:"submit",loading:v,children:"add"===u?v?"Adding...":"Add Member":v?"Saving...":"Save Changes"})]})]})})}],276173)},294612,e=>{"use strict";var t=e.i(843476),r=e.i(100486),a=e.i(827252),o=e.i(213205),n=e.i(771674),s=e.i(464571),i=e.i(770914),l=e.i(291542),d=e.i(262218),u=e.i(592968),c=e.i(898586),m=e.i(902555);let{Text:p}=c.Typography;e.s(["default",0,function({members:e,canEdit:c,onEdit:f,onDelete:g,onAddMember:h,roleColumnTitle:b="Role",roleTooltip:v,extraColumns:x=[],showDeleteForMember:C,emptyText:y}){let w=[{title:"User Email",dataIndex:"user_email",key:"user_email",render:e=>(0,t.jsx)(p,{children:e||"-"})},{title:"User ID",dataIndex:"user_id",key:"user_id",render:e=>"default_user_id"===e?(0,t.jsx)(d.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(p,{children:e||"-"})},{title:v?(0,t.jsxs)(i.Space,{direction:"horizontal",children:[b,(0,t.jsx)(u.Tooltip,{title:v,children:(0,t.jsx)(a.InfoCircleOutlined,{})})]}):b,dataIndex:"role",key:"role",render:e=>(0,t.jsxs)(i.Space,{children:[e?.toLowerCase()==="admin"||e?.toLowerCase()==="org_admin"?(0,t.jsx)(r.CrownOutlined,{}):(0,t.jsx)(n.UserOutlined,{}),(0,t.jsx)(p,{style:{textTransform:"capitalize"},children:e||"-"})]})},...x,{title:"Actions",key:"actions",fixed:"right",width:120,render:(e,r)=>c?(0,t.jsxs)(i.Space,{children:[(0,t.jsx)(m.default,{variant:"Edit",tooltipText:"Edit member",dataTestId:"edit-member",onClick:()=>f(r)}),(!C||C(r))&&(0,t.jsx)(m.default,{variant:"Delete",tooltipText:"Delete member",dataTestId:"delete-member",onClick:()=>g(r)})]}):null}];return(0,t.jsxs)(i.Space,{direction:"vertical",style:{width:"100%"},children:[(0,t.jsxs)("span",{className:"inline-flex text-sm text-gray-700",children:[e.length," Member",1!==e.length?"s":""]}),(0,t.jsx)(l.Table,{columns:w,dataSource:e,rowKey:e=>e.user_id??e.user_email??JSON.stringify(e),pagination:!1,size:"small",scroll:{x:"max-content"},locale:y?{emptyText:y}:void 0}),h&&c&&(0,t.jsx)(s.Button,{icon:(0,t.jsx)(o.UserAddOutlined,{}),type:"primary",onClick:h,children:"Add Member"})]})}])},86827,e=>{"use strict";var t=e.i(843476),r=e.i(482725),a=e.i(56456);e.s(["AntDLoadingSpinner",0,function({size:e,fontSize:o}){let n=(0,t.jsx)(a.LoadingOutlined,{style:o?{fontSize:o}:void 0,spin:!0});return(0,t.jsx)(r.Spin,{indicator:n,size:e})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02zbkoezzcnn1.js b/litellm/proxy/_experimental/out/_next/static/chunks/02zbkoezzcnn1.js new file mode 100644 index 00000000000..62af09f3759 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02zbkoezzcnn1.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let n=l(e);t(n),r.current=n,o&&o({current:n})};var s=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},b=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,c.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:l,transitionStatus:n})=>{let i=l?r===s.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(u,{className:(0,d.tremorTwMerge)(f("icon"),"animate-spin shrink-0",i,m.default,m[n]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(f("icon"),"shrink-0",t,i)})},p=a.default.forwardRef((e,o)=>{let{icon:u,iconPosition:m=s.HorizontalPositions.Left,size:p=s.Sizes.SM,color:C,variant:x="primary",disabled:k,loading:v=!1,loadingText:w,children:$,tooltip:N,className:j}=e,y=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),T=v||k,B=void 0!==u||v,E=v&&w,O=!(!$&&!E),M=(0,d.tremorTwMerge)(g[p].height,g[p].width),S="light"!==x?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=b(x,C),P=("light"!==x?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[p],{tooltipProps:z,getReferenceProps:H}=(0,r.useTooltip)(300),[q,A]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:s,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,b]=(0,a.useState)(()=>l(d?2:n(c))),f=(0,a.useRef)(g),h=(0,a.useRef)(0),[p,C]="object"==typeof s?[s.enter,s.exit]:[s,s],x=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(f.current._s,u);e&&i(e,b,f,h,m)},[m,u]);return[g,(0,a.useCallback)(a=>{let l=e=>{switch(i(e,b,f,h,m),e){case 1:p>=0&&(h.current=((...e)=>setTimeout(...e))(x,p));break;case 4:C>=0&&(h.current=((...e)=>setTimeout(...e))(x,C));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},s=f.current.isEnter;"boolean"!=typeof a&&(a=!s),a?s||l(e?+!r:2):s&&l(t?o?3:4:n(u))},[x,m,e,t,r,o,p,C,u]),x]})({timeout:50});return(0,a.useEffect)(()=>{A(v)},[v]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([o,z.refs.setReference]),className:(0,d.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",S,P.paddingX,P.paddingY,P.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,T?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(b(x,C).hoverTextColor,b(x,C).hoverBgColor,b(x,C).hoverBorderColor),j),disabled:T},H,y),a.default.createElement(r.default,Object.assign({text:N},z)),B&&m!==s.HorizontalPositions.Right?a.default.createElement(h,{loading:v,iconSize:M,iconPosition:m,Icon:u,transitionStatus:q.status,needMargin:O}):null,E||$?a.default.createElement("span",{className:(0,d.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},E?w:$):null,B&&m===s.HorizontalPositions.Right?a.default.createElement(h,{loading:v,iconSize:M,iconPosition:m,Icon:u,transitionStatus:q.status,needMargin:O}):null)});p.displayName="Button",e.s(["Button",0,p],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),o=e.i(271645);let l=o.default.forwardRef((e,l)=>{let{color:n,className:i,children:s}=e;return o.default.createElement("p",{ref:l,className:(0,r.tremorTwMerge)("text-tremor-default",n?(0,a.getColorClassNames)(n,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},s)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),l=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,n.getColorClassNames)(c,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});s.displayName="Card",e.s(["Card",0,s],304967)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},s),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",i)},s),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},s),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},s),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),i)},s),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),o=e.i(529681);let l=e=>{let{prefixCls:a,className:o,style:l,size:n,shape:i}=e,s=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),d=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),c=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,s,d,o),style:Object.assign(Object.assign({},c),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,i.unit)(e)}),m=e=>Object.assign({width:e},u(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),b=e=>Object.assign({width:e},u(e)),f=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),p=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:o,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:s,controlHeightLG:d,controlHeightSM:u,gradientFromColor:p,padding:C,marginSM:x,borderRadius:k,titleHeight:v,blockRadius:w,paragraphLiHeight:$,controlHeightXS:N,paragraphMarginTop:j}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:C,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:p},m(s)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},m(d)),[`${r}-sm`]:Object.assign({},m(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:v,background:p,borderRadius:w,[`+ ${o}`]:{marginBlockStart:u}},[o]:{padding:0,"> li":{width:"100%",height:$,listStyle:"none",background:p,borderRadius:w,"+ li":{marginBlockStart:N}}},[`${o}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${o} > li`]:{borderRadius:k}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:x,[`+ ${o}`]:{marginBlockStart:j}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},f(e,a,r)),{[`${r}-lg`]:Object.assign({},h(o,i))}),f(e,o,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),f(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},m(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},m(o)),[`${t}${t}-sm`]:Object.assign({},m(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(o,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:o,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:o},b(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${o} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),C=e=>{let{prefixCls:a,className:o,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,o),style:l},i)},x=({prefixCls:e,className:a,width:o,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:o},l)});function k(e){return e&&"object"==typeof e?e:{}}let v=e=>{let{prefixCls:o,loading:n,className:i,rootClassName:s,style:d,children:c,avatar:u=!1,title:m=!0,paragraph:g=!0,active:b,round:f}=e,{getPrefixCls:h,direction:v,className:w,style:$}=(0,a.useComponentConfig)("skeleton"),N=h("skeleton",o),[j,y,T]=p(N);if(n||!("loading"in e)){let e,a,o=!!u,n=!!m,c=!!g;if(o){let r=Object.assign(Object.assign({prefixCls:`${N}-avatar`},n&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),k(u));e=t.createElement("div",{className:`${N}-header`},t.createElement(l,Object.assign({},r)))}if(n||c){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${N}-title`},!o&&c?{width:"38%"}:o&&c?{width:"50%"}:{}),k(m));e=t.createElement(x,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${N}-paragraph`},(e={},o&&n||(e.width="61%"),!o&&n?e.rows=3:e.rows=2,e)),k(g));r=t.createElement(C,Object.assign({},a))}a=t.createElement("div",{className:`${N}-content`},e,r)}let h=(0,r.default)(N,{[`${N}-with-avatar`]:o,[`${N}-active`]:b,[`${N}-rtl`]:"rtl"===v,[`${N}-round`]:f},w,i,s,y,T);return j(t.createElement("div",{className:h,style:Object.assign(Object.assign({},$),d)},e,a))}return null!=c?c:null};v.Button=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c=!1,size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",n),[b,f,h]=p(g),C=(0,o.default)(e,["prefixCls"]),x=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d,[`${g}-block`]:c},i,s,f,h);return b(t.createElement("div",{className:x},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:u},C))))},v.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,shape:c="circle",size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",n),[b,f,h]=p(g),C=(0,o.default)(e,["prefixCls","className"]),x=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d},i,s,f,h);return b(t.createElement("div",{className:x},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:c,size:u},C))))},v.Input=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c,size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",n),[b,f,h]=p(g),C=(0,o.default)(e,["prefixCls"]),x=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d,[`${g}-block`]:c},i,s,f,h);return b(t.createElement("div",{className:x},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:u},C))))},v.Image=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",o),[u,m,g]=p(c),b=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:s},l,n,m,g);return u(t.createElement("div",{className:b},t.createElement("div",{className:(0,r.default)(`${c}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},v.Node=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),u=c("skeleton",o),[m,g,b]=p(u),f=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:s},g,l,n,b);return m(t.createElement("div",{className:f},t.createElement("div",{className:(0,r.default)(`${u}-image`,l),style:i},d)))},e.s(["default",0,v],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let o=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(o),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let o={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",o);let l=e<0?"-":"",n=Math.abs(e),i=n,s="";return n>=1e6?(i=n/1e6,s="M"):n>=1e3&&(i=n/1e3,s="K"),`${l}${i.toLocaleString("en-US",o)}${s}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return o(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),o(e,r)}},o=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let o=document.execCommand("copy");if(document.body.removeChild(a),o)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),o=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(o.TooltipProvider,{delay:300,children:(0,t.jsxs)(o.Tooltip,{children:[(0,t.jsx)(o.TooltipTrigger,{render:r}),(0,t.jsx)(o.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:o,tooltip:i,dataTestId:s}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":s,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:o});return i?(0,t.jsx)(l,{content:i,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`,`${s}, ${d} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:o,copyable:d=!1,truncate:c=!0,fallback:u="-",tooltip:m,disabled:g=!1,dataTestId:b,className:f}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:u});let h=!!o&&!g,p=(0,n.cn)(s[a].base,h&&s[a].clickable,c&&"block max-w-[15ch] truncate",g&&"opacity-50",f),C=h?(0,r.jsx)("button",{type:"button",className:p,"data-testid":b,onClick:()=>o(e),children:e}):(0,r.jsx)("span",{className:p,"data-testid":b,children:e}),x=(0,r.jsx)(t.CellTooltip,{content:m??e,trigger:C});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[x,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):x}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:o=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?o?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03-4f3.602g1r.js b/litellm/proxy/_experimental/out/_next/static/chunks/03-4f3.602g1r.js new file mode 100644 index 00000000000..642068f78e4 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03-4f3.602g1r.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,555987,e=>{"use strict";var t=e.i(221688),i=e.i(950643);let n=/^(https?:|data:|blob:|\/\/)/i;e.s(["resolveLogoSrc",0,(e,o=t.serverRootPath)=>{if(e){let t;return n.test(e)?e:(t=(0,i.normalizeRootPath)(o),`${t}${e.startsWith("/")?e:`/${e}`}`)}}])},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,a){return t.createElement(o.default,(0,i.default)({},e,{ref:a,icon:n}))});let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var r=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:l}))}),c=e.i(801312),u=e.i(286612),s=e.i(343794),d=e.i(211577),m=e.i(410160),g=e.i(209428),p=e.i(392221),f=e.i(914949),b=e.i(404948),v=e.i(244009);e.i(883110);let h={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var $=[10,20,50,100];let S=function(e){var i=e.pageSizeOptions,n=void 0===i?$:i,o=e.locale,a=e.changeSize,l=e.pageSize,r=e.goButton,c=e.quickGo,u=e.rootPrefixCls,s=e.disabled,d=e.buildOptionText,m=e.showSizeChanger,g=e.sizeChangerRender,f=t.default.useState(""),v=(0,p.default)(f,2),h=v[0],S=v[1],C=function(){return!h||Number.isNaN(h)?void 0:Number(h)},k="function"==typeof d?d:function(e){return"".concat(e," ").concat(o.items_per_page)},y=function(e){""!==h&&(e.keyCode===b.default.ENTER||"click"===e.type)&&(S(""),null==c||c(C()))},x="".concat(u,"-options");if(!m&&!c)return null;var z=null,E=null,N=null;return m&&g&&(z=g({disabled:s,size:l,onSizeChange:function(e){null==a||a(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===l.toString()})?n:n.concat([l]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:k(e),value:e}})})),c&&(r&&(N="boolean"==typeof r?t.default.createElement("button",{type:"button",onClick:y,onKeyUp:y,disabled:s,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:y,onKeyUp:y},r)),E=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:s,type:"text",value:h,onChange:function(e){S(e.target.value)},onKeyUp:y,onBlur:function(e){r||""===h||(S(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(u,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(u,"-item"))>=0)||null==c||c(C()))},"aria-label":o.page}),o.page,N)),t.default.createElement("li",{className:x},z,E)},C=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,a=e.className,l=e.showTitle,r=e.onClick,c=e.onKeyPress,u=e.itemRender,m="".concat(i,"-item"),g=(0,s.default)(m,"".concat(m,"-").concat(n),(0,d.default)((0,d.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),a),p=u(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return p?t.default.createElement("li",{title:l?String(n):null,className:g,onClick:function(){r(n)},onKeyDown:function(e){c(e,r,n)},tabIndex:0},p):null};var k=function(e,t,i){return i};function y(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function z(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let E=function(e){var n,o,a,l,r=e.prefixCls,c=void 0===r?"rc-pagination":r,u=e.selectPrefixCls,$=e.className,E=e.current,N=e.defaultCurrent,j=e.total,B=void 0===j?0:j,M=e.pageSize,O=e.defaultPageSize,w=e.onChange,I=void 0===w?y:w,T=e.hideOnSinglePage,P=e.align,D=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,R=e.showTitle,_=void 0===R||R,W=e.onShowSizeChange,L=void 0===W?y:W,q=e.locale,K=void 0===q?h:q,X=e.style,U=e.totalBoundaryShowSizeChanger,F=e.disabled,J=e.simple,G=e.showTotal,V=e.showSizeChanger,Q=void 0===V?B>(void 0===U?50:U):V,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?k:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,ea=e.nextIcon,el=t.default.useRef(null),er=(0,f.default)(10,{value:M,defaultValue:void 0===O?10:O}),ec=(0,p.default)(er,2),eu=ec[0],es=ec[1],ed=(0,f.default)(1,{value:E,defaultValue:void 0===N?1:N,postState:function(e){return Math.max(1,Math.min(e,z(void 0,eu,B)))}}),em=(0,p.default)(ed,2),eg=em[0],ep=em[1],ef=t.default.useState(eg),eb=(0,p.default)(ef,2),ev=eb[0],eh=eb[1];(0,t.useEffect)(function(){eh(eg)},[eg]);var e$=Math.max(1,eg-(A?3:5)),eS=Math.min(z(void 0,eu,B),eg+(A?3:5));function eC(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,g.default)({},e))),o}function ek(e){var t=e.target.value,i=z(void 0,eu,B);return""===t?t:Number.isNaN(Number(t))?ev:t>=i?i:Number(t)}var ey=B>eu&&H;function ex(e){var t=ek(e);switch(t!==ev&&eh(t),e.keyCode){case b.default.ENTER:ez(t);break;case b.default.UP:ez(t-1);break;case b.default.DOWN:ez(t+1)}}function ez(e){if(x(e)&&e!==eg&&x(B)&&B>0&&!F){var t=z(void 0,eu,B),i=e;return e>t?i=t:e<1&&(i=1),i!==ev&&eh(i),ep(i),null==I||I(i,eu),i}return eg}var eE=eg>1,eN=eg2?i-2:0),o=2;oB?B:eg*eu])),eH=null,eA=z(void 0,eu,B);if(T&&B<=eu)return null;var eR=[],e_={rootPrefixCls:c,onClick:ez,onKeyPress:ew,showTitle:_,itemRender:et,page:-1},eW=eg-1>0?eg-1:0,eL=eg+1=2*eF&&3!==eg&&(eR[0]=t.default.cloneElement(eR[0],{className:(0,s.default)("".concat(c,"-item-after-jump-prev"),eR[0].props.className)}),eR.unshift(eT)),eA-eg>=2*eF&&eg!==eA-2){var e2=eR[eR.length-1];eR[eR.length-1]=t.default.cloneElement(e2,{className:(0,s.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),eR.push(eH)}1!==eZ&&eR.unshift(t.default.createElement(C,(0,i.default)({},e_,{key:1,page:1}))),e0!==eA&&eR.push(t.default.createElement(C,(0,i.default)({},e_,{key:eA,page:eA})))}var e4=(n=et(eW,"prev",eC(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!eE}):n);if(e4){var e6=!eE||!eA;e4=t.default.createElement("li",{title:_?K.prev_page:null,onClick:ej,tabIndex:e6?null:0,onKeyDown:function(e){ew(e,ej)},className:(0,s.default)("".concat(c,"-prev"),(0,d.default)({},"".concat(c,"-disabled"),e6)),"aria-disabled":e6},e4)}var e3=(o=et(eL,"next",eC(ea,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eN}):o);e3&&(J?(a=!eN,l=eE?0:null):l=(a=!eN||!eA)?null:0,e3=t.default.createElement("li",{title:_?K.next_page:null,onClick:eB,tabIndex:l,onKeyDown:function(e){ew(e,eB)},className:(0,s.default)("".concat(c,"-next"),(0,d.default)({},"".concat(c,"-disabled"),a)),"aria-disabled":a},e3));var e9=(0,s.default)(c,$,(0,d.default)((0,d.default)((0,d.default)((0,d.default)((0,d.default)({},"".concat(c,"-start"),"start"===P),"".concat(c,"-center"),"center"===P),"".concat(c,"-end"),"end"===P),"".concat(c,"-simple"),J),"".concat(c,"-disabled"),F));return t.default.createElement("ul",(0,i.default)({className:e9,style:X,ref:el},eP),eD,e4,J?eU:eR,e3,t.default.createElement(S,{locale:K,rootPrefixCls:c,disabled:F,selectPrefixCls:void 0===u?"rc-select":u,changeSize:function(e){var t=z(e,eu,B),i=eg>t&&0!==t?t:eg;es(e),eh(i),null==L||L(eg,e),ep(i),null==I||I(i,e)},pageSize:eu,pageSizeOptions:Z,quickGo:ey?ez:null,goButton:eX,showSizeChanger:Q,sizeChangerRender:Y}))};var N=e.i(727214),j=e.i(242064),B=e.i(517455),M=e.i(150073),O=e.i(408850),w=e.i(327494),I=e.i(104458);e.i(296059);var T=e.i(915654),P=e.i(349942),D=e.i(517458),H=e.i(889943),A=e.i(183293),R=e.i(246422),_=e.i(838378);let W=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,D.initComponentToken)(e)),L=e=>(0,_.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,D.initInputToken)(e)),q=(0,R.genStyleHooks)("Pagination",e=>{let t=L(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,T.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,P.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,T.unit)(e.inputOutlineOffset)} 0 ${(0,T.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,P.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},W),K=(0,R.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(L(e)),W);function X(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var U=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:l,rootClassName:d,style:m,size:g,locale:p,responsive:f,showSizeChanger:b,selectComponentClass:v,pageSizeOptions:h}=e,$=U(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:S}=(0,M.default)(f),[,C]=(0,I.useToken)(),{getPrefixCls:k,direction:y,showSizeChanger:x,className:z,style:T}=(0,j.useComponentConfig)("pagination"),P=k("pagination",n),[D,H,A]=q(P),R=(0,B.default)(g),_="small"===R||!!(S&&!R&&f),[W]=(0,O.useLocale)("Pagination",N.default),L=Object.assign(Object.assign({},W),p),[F,J]=X(b),[G,V]=X(x),Q=null!=J?J:V,Y=v||w.default,Z=t.useMemo(()=>h?h.map(e=>Number(e)):void 0,[h]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${P}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(u.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(c.default,null):t.createElement(u.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(r,{className:`${P}-item-link-icon`}):t.createElement(a,{className:`${P}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(a,{className:`${P}-item-link-icon`}):t.createElement(r,{className:`${P}-item-link-icon`}),e))}},[y,P]),et=k("select",o),ei=(0,s.default)({[`${P}-${i}`]:!!i,[`${P}-mini`]:_,[`${P}-rtl`]:"rtl"===y,[`${P}-bordered`]:C.wireframe},z,l,d,H,A),en=Object.assign(Object.assign({},T),m);return D(t.createElement(t.Fragment,null,C.wireframe&&t.createElement(K,{prefixCls:P}),t.createElement(E,Object.assign({},ee,$,{style:en,prefixCls:P,selectPrefixCls:et,className:ei,locale:L,pageSizeOptions:Z,showSizeChanger:null!=F?F:G,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:a,"aria-label":l,className:r,options:c}=e,{className:u,onChange:d}=Q||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":l,options:c},Q,{value:m,onChange:(e,t)=>{null==a||a(e),null==d||d(e,t)},size:_?"small":"middle",className:(0,s.default)(r,u)}))}}))))}],165370)},285027,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"}}]},name:"warning",theme:"outlined"};var o=e.i(9583),a=i.forwardRef(function(e,a){return i.createElement(o.default,(0,t.default)({},e,{ref:a,icon:n}))});e.s(["WarningOutlined",0,a],285027)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/032wf1_8kb1mb.js b/litellm/proxy/_experimental/out/_next/static/chunks/032wf1_8kb1mb.js new file mode 100644 index 00000000000..d56acb17af6 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/032wf1_8kb1mb.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,40992,t=>{"use strict";t.s(["default",0,function(t,n){if(!t)throw Error("Invariant failed")}])},475058,t=>{"use strict";t.s(["default",0,function(t){return function(){return t}}])},216888,t=>{"use strict";let n=Math.PI,e=2*n,r=e-1e-6;function i(t){this._+=t[0];for(let n=1,e=t.length;n=0))throw Error(`invalid digits: ${t}`);if(n>15)return i;let e=10**n;return function(t){this._+=t[0];for(let n=1,r=t.length;n1e-6)if(Math.abs(f*s-l*c)>1e-6&&o){let g=r-u,d=i-a,p=s*s+l*l,y=Math.sqrt(p),x=Math.sqrt(h),v=o*Math.tan((n-Math.acos((p+h-(g*g+d*d))/(2*y*x)))/2),_=v/x,m=v/y;Math.abs(_-1)>1e-6&&this._append`L${t+_*c},${e+_*f}`,this._append`A${o},${o},0,0,${+(f*g>c*d)},${this._x1=t+m*s},${this._y1=e+m*l}`}else this._append`L${this._x1=t},${this._y1=e}`}arc(t,i,o,u,a,s){if(t*=1,i*=1,o*=1,s=!!s,o<0)throw Error(`negative radius: ${o}`);let l=o*Math.cos(u),c=o*Math.sin(u),f=t+l,h=i+c,g=1^s,d=s?u-a:a-u;null===this._x1?this._append`M${f},${h}`:(Math.abs(this._x1-f)>1e-6||Math.abs(this._y1-h)>1e-6)&&this._append`L${f},${h}`,o&&(d<0&&(d=d%e+e),d>r?this._append`A${o},${o},0,1,${g},${t-l},${i-c}A${o},${o},0,1,${g},${this._x1=f},${this._y1=h}`:d>1e-6&&this._append`A${o},${o},0,${+(d>=n)},${g},${this._x1=t+o*Math.cos(a)},${this._y1=i+o*Math.sin(a)}`)}rect(t,n,e,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${e*=1}v${+r}h${-e}Z`}toString(){return this._}}o.prototype,t.s(["withPath",0,function(t){let n=3;return t.digits=function(e){if(!arguments.length)return n;if(null==e)n=null;else{let t=Math.floor(e);if(!(t>=0))throw RangeError(`invalid digits: ${e}`);n=t}return t},()=>new o(n)}],216888)},464085,274035,486529,219517,315939,749502,841748,415333,t=>{"use strict";var n=t.i(475058),e=t.i(216888);let r=Math.cos,i=Math.sin,o=Math.sqrt,u=Math.PI,a=2*u;o(3);let s={draw(t,n){let e=o(n/u);t.moveTo(e,0),t.arc(0,0,e,0,a)}},l=o(1/3),c=2*l,f=i(u/10)/i(7*u/10),h=i(a/10)*f,g=-r(a/10)*f,d=o(3);o(3);let p=o(3)/2,y=1/o(12),x=(y/2+1)*3;t.s(["symbol",0,function(t,r){let i=null,o=(0,e.withPath)(u);function u(){let n;if(i||(i=n=o()),t.apply(this,arguments).draw(i,+r.apply(this,arguments)),n)return i=null,n+""||null}return t="function"==typeof t?t:(0,n.default)(t||s),r="function"==typeof r?r:(0,n.default)(void 0===r?64:+r),u.type=function(e){return arguments.length?(t="function"==typeof e?e:(0,n.default)(e),u):t},u.size=function(t){return arguments.length?(r="function"==typeof t?t:(0,n.default)(+t),u):r},u.context=function(t){return arguments.length?(i=null==t?null:t,u):i},u}],464085),t.s(["symbolCircle",0,s],274035),t.s(["symbolCross",0,{draw(t,n){let e=o(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}}],486529),t.s(["symbolDiamond",0,{draw(t,n){let e=o(n/c),r=e*l;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}}],219517),t.s(["symbolSquare",0,{draw(t,n){let e=o(n),r=-e/2;t.rect(r,r,e,e)}}],315939),t.s(["symbolStar",0,{draw(t,n){let e=o(.8908130915292852*n),u=h*e,s=g*e;t.moveTo(0,-e),t.lineTo(u,s);for(let n=1;n<5;++n){let o=a*n/5,l=r(o),c=i(o);t.lineTo(c*e,-l*e),t.lineTo(l*u-c*s,c*u+l*s)}t.closePath()}}],749502),t.s(["symbolTriangle",0,{draw(t,n){let e=-o(n/(3*d));t.moveTo(0,2*e),t.lineTo(-d*e,-e),t.lineTo(d*e,-e),t.closePath()}}],841748),t.s(["symbolWye",0,{draw(t,n){let e=o(n/x),r=e/2,i=e*y,u=e*y+e,a=-r;t.moveTo(r,i),t.lineTo(r,u),t.lineTo(a,u),t.lineTo(-.5*r-p*i,p*r+-.5*i),t.lineTo(-.5*r-p*u,p*r+-.5*u),t.lineTo(-.5*a-p*u,p*a+-.5*u),t.lineTo(-.5*r+p*i,-.5*i-p*r),t.lineTo(-.5*r+p*u,-.5*u-p*r),t.lineTo(-.5*a+p*u,-.5*u-p*a),t.closePath()}}],415333)},182984,365332,65232,t=>{"use strict";function n(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}t.s(["initInterpolator",0,function(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this},"initRange",0,n],365332);class e extends Map{constructor(t,n=i){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(r(this,t))}has(t){return super.has(r(this,t))}set(t,n){return super.set(function({_intern:t,_key:n},e){let r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}(this,t),n)}delete(t){return super.delete(function({_intern:t,_key:n},e){let r=n(e);return t.has(r)&&(e=t.get(r),t.delete(r)),e}(this,t))}}function r({_intern:t,_key:n},e){let i=n(e);return t.has(i)?t.get(i):e}function i(t){return null!==t&&"object"==typeof t?t.valueOf():t}let o=Symbol("implicit");function u(){var t=new e,r=[],i=[],a=o;function s(n){let e=t.get(n);if(void 0===e){if(a!==o)return a;t.set(n,e=r.push(n)-1)}return i[e%i.length]}return s.domain=function(n){if(!arguments.length)return r.slice();for(let i of(r=[],t=new e,n))t.has(i)||t.set(i,r.push(i)-1);return s},s.range=function(t){return arguments.length?(i=Array.from(t),s):i.slice()},s.unknown=function(t){return arguments.length?(a=t,s):a},s.copy=function(){return u(r,i).unknown(a)},n.apply(s,arguments),s}function a(){var t,e,r=u().unknown(void 0),i=r.domain,o=r.range,s=0,l=1,c=!1,f=0,h=0,g=.5;function d(){var n=i().length,r=l{"use strict";t.s([])},429061,t=>{"use strict";t.i(267155);var n,e,r,i,o,u,a,s=t.i(182984);let l=Math.sqrt(50),c=Math.sqrt(10),f=Math.sqrt(2);function h(t,n,e){let r,i,o,u=(n-t)/Math.max(0,e),a=Math.floor(Math.log10(u)),s=u/Math.pow(10,a),g=s>=l?10:s>=c?5:s>=f?2:1;return(a<0?(r=Math.round(t*(o=Math.pow(10,-a)/g)),i=Math.round(n*o),r/on&&--i,o=-o):(r=Math.round(t/(o=Math.pow(10,a)*g)),i=Math.round(n/o),r*on&&--i),i0))return[];if(t===n)return[t];let r=n=i))return[];let a=o-i+1,s=Array(a);if(r)if(u<0)for(let t=0;tn?1:t>=n?0:NaN}function x(t,n){return null==t||null==n?NaN:nt?1:n>=t?0:NaN}function v(t){let n,e,r;function i(t,r,o=0,u=t.length){if(o>>1;0>e(t[n],r)?o=n+1:u=n}while(oy(t(n),e),r=(n,e)=>t(n)-e):(n=t===y||t===x?t:_,e=t,r=t),{left:i,center:function(t,n,e=0,o=t.length){let u=i(t,n,e,o-1);return u>e&&r(t[u-1],n)>-r(t[u],n)?u-1:u},right:function(t,r,i=0,o=t.length){if(i>>1;0>=e(t[n],r)?i=n+1:o=n}while(i>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?R(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?R(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=U.exec(t))?new j(n[1],n[2],n[3],1):(n=E.exec(t))?new j(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=S.exec(t))?R(n[1],n[2],n[3],n[4]):(n=A.exec(t))?R(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=F.exec(t))?X(n[1],n[2]/100,n[3]/100,1):(n=O.exec(t))?X(n[1],n[2]/100,n[3]/100,n[4]):q.hasOwnProperty(t)?Y(q[t]):"transparent"===t?new j(NaN,NaN,NaN,0):null}function Y(t){return new j(t>>16&255,t>>8&255,255&t,1)}function R(t,n,e,r){return r<=0&&(t=n=e=NaN),new j(t,n,e,r)}function I(t,n,e,r){var i;return 1==arguments.length?((i=t)instanceof N||(i=H(i)),i)?new j((i=i.rgb()).r,i.g,i.b,i.opacity):new j:new j(t,n,e,null==r?1:r)}function j(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function z(){return`#${V(this.r)}${V(this.g)}${V(this.b)}`}function Z(){let t=B(this.opacity);return`${1===t?"rgb(":"rgba("}${W(this.r)}, ${W(this.g)}, ${W(this.b)}${1===t?")":`, ${t})`}`}function B(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function W(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function V(t){return((t=W(t))<16?"0":"")+t.toString(16)}function X(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new J(t,n,e,r)}function Q(t){if(t instanceof J)return new J(t.h,t.s,t.l,t.opacity);if(t instanceof N||(t=H(t)),!t)return new J;if(t instanceof J)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,s=(o+i)/2;return a?(u=n===o?(e-r)/a+(e0&&s<1?0:u,new J(u,a,s,t.opacity)}function J(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function G(t){return(t=(t||0)%360)<0?t+360:t}function K(t){return Math.max(0,Math.min(1,t||0))}function tt(t,n,e){return(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)*255}function tn(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}b(N,H,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:P,formatHex:P,formatHex8:function(){return this.rgb().formatHex8()},formatHsl:function(){return Q(this).formatHsl()},formatRgb:L,toString:L}),b(j,I,T(N,{brighter(t){return t=null==t?1.4285714285714286:Math.pow(1.4285714285714286,t),new j(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=null==t?.7:Math.pow(.7,t),new j(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new j(W(this.r),W(this.g),W(this.b),B(this.opacity))},displayable(){return -.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:z,formatHex:z,formatHex8:function(){return`#${V(this.r)}${V(this.g)}${V(this.b)}${V((isNaN(this.opacity)?1:this.opacity)*255)}`},formatRgb:Z,toString:Z})),b(J,function(t,n,e,r){return 1==arguments.length?Q(t):new J(t,n,e,null==r?1:r)},T(N,{brighter(t){return t=null==t?1.4285714285714286:Math.pow(1.4285714285714286,t),new J(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?.7:Math.pow(.7,t),new J(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new j(tt(t>=240?t-240:t+120,i,r),tt(t,i,r),tt(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new J(G(this.h),K(this.s),K(this.l),B(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let t=B(this.opacity);return`${1===t?"hsl(":"hsla("}${G(this.h)}, ${100*K(this.s)}%, ${100*K(this.l)}%${1===t?")":`, ${t})`}`}}));let te=t=>()=>t;function tr(t,n){var e=n-t;return e?function(n){return t+n*e}:te(isNaN(t)?n:t)}let ti=function t(n){var e,r=1==(e=+n)?tr:function(t,n){var r,i,o;return n-t?(r=t,i=n,r=Math.pow(r,o=e),i=Math.pow(i,o)-r,o=1/o,function(t){return Math.pow(r+t*i,o)}):te(isNaN(t)?n:t)};function i(t,n){var e=r((t=I(t)).r,(n=I(n)).r),i=r(t.g,n.g),o=r(t.b,n.b),u=tr(t.opacity,n.opacity);return function(n){return t.r=e(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}return i.gamma=t,i}(1);function to(t){return function(n){var e,r,i=n.length,o=Array(i),u=Array(i),a=Array(i);for(e=0;e=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=ra&&(u=n.slice(a,u),l[s]?l[s]+=u:l[++s]=u),(i=i[0])===(o=o[0])?l[s]?l[s]+=o:l[++s]=o:(l[++s]=null,c.push({i:s,x:tu(i,o)})),a=ts.lastIndex;return an&&(e=t,t=n,n=e),l=function(e){return Math.max(t,Math.min(n,e))}),r=s>2?ty:tp,i=o=null,f}function f(n){return null==n||isNaN(n*=1)?e:(i||(i=r(u.map(t),a,s)))(t(l(n)))}return f.invert=function(e){return l(n((o||(o=r(a,u.map(t),tu)))(e)))},f.domain=function(t){return arguments.length?(u=Array.from(t,tf),c()):u.slice()},f.range=function(t){return arguments.length?(a=Array.from(t),c()):a.slice()},f.rangeRound=function(t){return a=Array.from(t),s=tc,c()},f.clamp=function(t){return arguments.length?(l=!!t||tg,c()):l!==tg},f.interpolate=function(t){return arguments.length?(s=t,c()):s},f.unknown=function(t){return arguments.length?(e=t,f):e},function(e,r){return t=e,n=r,c()}}function t_(){return tv()(tg,tg)}var tm=t.i(365332);function tM(t,n){if(!isFinite(t)||0===t)return null;var e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"),r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function tw(t){return(t=tM(Math.abs(t)))?t[1]:NaN}var tb=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function tT(t){var n;if(!(n=tb.exec(t)))throw Error("invalid format: "+t);return new tN({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function tN(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function tk(t,n){var e=tM(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+Array(i-r.length+2).join("0")}tT.prototype=tN.prototype,tN.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};let t$={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>tk(100*t,n),r:tk,s:function(t,e){var r=tM(t,e);if(!r)return n=void 0,t.toPrecision(e);var i=r[0],o=r[1],u=o-(n=3*Math.max(-8,Math.min(8,Math.floor(o/3))))+1,a=i.length;return u===a?i:u>a?i+Array(u-a+1).join("0"):u>0?i.slice(0,u)+"."+i.slice(u):"0."+Array(1-u).join("0")+tM(t,Math.max(0,e+u-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function tC(t){return t}var tD=Array.prototype.map,tU=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function tE(t,n,e,o){var u,a,s=p(t,n,e);switch((o=tT(null==o?",f":o)).type){case"s":var l=Math.max(Math.abs(t),Math.abs(n));return null!=o.precision||isNaN(a=Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(tw(l)/3)))-tw(Math.abs(s))))||(o.precision=a),i(o,l);case"":case"e":case"g":case"p":case"r":null!=o.precision||isNaN(a=Math.max(0,tw(Math.abs(Math.max(Math.abs(t),Math.abs(n)))-(u=Math.abs(u=s)))-tw(u))+1)||(o.precision=a-("e"===o.type));break;case"f":case"%":null!=o.precision||isNaN(a=Math.max(0,-tw(Math.abs(s))))||(o.precision=a-("%"===o.type)*2)}return r(o)}function tS(t){var n=t.domain;return t.ticks=function(t){var e=n();return g(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return tE(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),u=0,a=o.length-1,s=o[u],l=o[a],c=10;for(l0;){if((i=d(s,l,e))===r)return o[u]=s,o[a]=l,n(o);if(i>0)s=Math.floor(s/i)*i,l=Math.ceil(l/i)*i;else if(i<0)s=Math.ceil(s*i)/i,l=Math.floor(l*i)/i;else break;r=i}return t},t}function tA(t,n){t=t.slice();var e,r=0,i=t.length-1,o=t[r],u=t[i];return u-t(-n,e)}function tY(t){let n,e,i=t(tF,tO),o=i.domain,u=10;function a(){var r,a;return n=(r=u)===Math.E?Math.log:10===r&&Math.log10||2===r&&Math.log2||(r=Math.log(r),t=>Math.log(t)/r),e=10===(a=u)?tL:a===Math.E?Math.exp:t=>Math.pow(a,t),o()[0]<0?(n=tH(n),e=tH(e),t(tq,tP)):t(tF,tO),i}return i.base=function(t){return arguments.length?(u=+t,a()):u},i.domain=function(t){return arguments.length?(o(t),a()):o()},i.ticks=t=>{let r,i,a=o(),s=a[0],l=a[a.length-1],c=l0){for(;f<=h;++f)for(r=1;rl)break;p.push(i)}}else for(;f<=h;++f)for(r=u-1;r>=1;--r)if(!((i=f>0?r/e(-f):r*e(f))l)break;p.push(i)}2*p.length{if(null==t&&(t=10),null==o&&(o=10===u?"s":","),"function"!=typeof o&&(u%1||null!=(o=tT(o)).precision||(o.trim=!0),o=r(o)),t===1/0)return o;let a=Math.max(1,u*t/i.ticks().length);return t=>{let r=t/e(Math.round(n(t)));return r*uo(tA(o(),{floor:t=>e(Math.floor(n(t))),ceil:t=>e(Math.ceil(n(t)))})),i}function tR(t){return function(n){return Math.sign(n)*Math.log1p(Math.abs(n/t))}}function tI(t){return function(n){return Math.sign(n)*Math.expm1(Math.abs(n))*t}}function tj(t){var n=1,e=t(tR(1),tI(n));return e.constant=function(e){return arguments.length?t(tR(n=+e),tI(n)):n},tS(e)}r=(e=function(t){var e,r,i,o=void 0===t.grouping||void 0===t.thousands?tC:(e=tD.call(t.grouping,Number),r=t.thousands+"",function(t,n){for(var i=t.length,o=[],u=0,a=e[0],s=0;i>0&&a>0&&(s+a+1>n&&(a=Math.max(1,n-s)),o.push(t.substring(i-=a,i+a)),!((s+=a+1)>n));)a=e[u=(u+1)%e.length];return o.reverse().join(r)}),u=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",s=void 0===t.decimal?".":t.decimal+"",l=void 0===t.numerals?tC:(i=tD.call(t.numerals,String),function(t){return t.replace(/[0-9]/g,function(t){return i[+t]})}),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",h=void 0===t.nan?"NaN":t.nan+"";function g(t,e){var r=(t=tT(t)).fill,i=t.align,g=t.sign,d=t.symbol,p=t.zero,y=t.width,x=t.comma,v=t.precision,_=t.trim,m=t.type;"n"===m?(x=!0,m="g"):t$[m]||(void 0===v&&(v=12),_=!0,m="g"),(p||"0"===r&&"="===i)&&(p=!0,r="0",i="=");var M=(e&&void 0!==e.prefix?e.prefix:"")+("$"===d?u:"#"===d&&/[boxX]/.test(m)?"0"+m.toLowerCase():""),w=("$"===d?a:/[%p]/.test(m)?c:"")+(e&&void 0!==e.suffix?e.suffix:""),b=t$[m],T=/[defgprs%]/.test(m);function N(t){var e,u,a,c=M,d=w;if("c"===m)d=b(t)+d,t="";else{var N=(t*=1)<0||1/t<0;if(t=isNaN(t)?h:b(Math.abs(t),v),_&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),N&&0==+t&&"+"!==g&&(N=!1),c=(N?"("===g?g:f:"-"===g||"("===g?"":g)+c,d=("s"!==m||isNaN(t)||void 0===n?"":tU[8+n/3])+d+(N&&"("===g?")":""),T){for(e=-1,u=t.length;++e(a=t.charCodeAt(e))||a>57){d=(46===a?s+t.slice(e+1):t.slice(e))+d,t=t.slice(0,e);break}}}x&&!p&&(t=o(t,1/0));var k=c.length+t.length+d.length,$=k>1)+c+t+d+$.slice(k);break;default:t=$+c+t+d}return l(t)}return v=void 0===v?6:/[gprs]/.test(m)?Math.max(1,Math.min(21,v)):Math.max(0,Math.min(20,v)),N.toString=function(){return t+""},N}return{format:g,formatPrefix:function(t,n){var e=3*Math.max(-8,Math.min(8,Math.floor(tw(n)/3))),r=Math.pow(10,-e),i=g(((t=tT(t)).type="f",t),{suffix:tU[8+e/3]});return function(t){return i(r*t)}}}}({thousands:",",grouping:[3],currency:["$",""]})).format,i=e.formatPrefix;var tz=t.i(65232);function tZ(t){return function(n){return n<0?-Math.pow(-n,t):Math.pow(n,t)}}function tB(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function tW(t){return t<0?-t*t:t*t}function tV(t){var n=t(tg,tg),e=1;return n.exponent=function(n){return arguments.length?1==(e=+n)?t(tg,tg):.5===e?t(tB,tW):t(tZ(e),tZ(1/e)):e},tS(n)}function tX(){var t=tV(tv());return t.copy=function(){return tx(t,tX()).exponent(t.exponent())},tm.initRange.apply(t,arguments),t}function tQ(t){return Math.sign(t)*t*t}function tJ(t,n){let e;if(void 0===n)for(let n of t)null!=n&&(e=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function tG(t,n){let e;if(void 0===n)for(let n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function tK(t,n){return(null==t||!(t>=t))-(null==n||!(n>=n))||(tn))}function t0(t,n,e){let r=t[n];t[n]=t[e],t[e]=r}let t1=new Date,t2=new Date;function t5(t,n,e,r){function i(n){return t(n=0==arguments.length?new Date:new Date(+n)),n}return i.floor=n=>(t(n=new Date(+n)),n),i.ceil=e=>(t(e=new Date(e-1)),n(e,1),t(e),e),i.round=t=>{let n=i(t),e=i.ceil(t);return t-n(n(t=new Date(+t),null==e?1:Math.floor(e)),t),i.range=(e,r,o)=>{let u,a=[];if(e=i.ceil(e),o=null==o?1:Math.floor(o),!(e0))return a;do a.push(u=new Date(+e)),n(e,o),t(e);while(ut5(n=>{if(n>=n)for(;t(n),!e(n);)n.setTime(n-1)},(t,r)=>{if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););}),e&&(i.count=(n,r)=>(t1.setTime(+n),t2.setTime(+r),t(t1),t(t2),Math.floor(e(t1,t2))),i.every=t=>isFinite(t=Math.floor(t))&&t>0?t>1?i.filter(r?n=>r(n)%t==0:n=>i.count(0,n)%t==0):i:null),i}let t3=t5(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,n)=>{t.setFullYear(t.getFullYear()+n)},(t,n)=>n.getFullYear()-t.getFullYear(),t=>t.getFullYear());t3.every=t=>isFinite(t=Math.floor(t))&&t>0?t5(n=>{n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},(n,e)=>{n.setFullYear(n.getFullYear()+e*t)}):null,t3.range;let t4=t5(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n)},(t,n)=>n.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());t4.every=t=>isFinite(t=Math.floor(t))&&t>0?t5(n=>{n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},(n,e)=>{n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null,t4.range;let t8=t5(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,n)=>{t.setMonth(t.getMonth()+n)},(t,n)=>n.getMonth()-t.getMonth()+(n.getFullYear()-t.getFullYear())*12,t=>t.getMonth());t8.range;let t6=t5(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCMonth(t.getUTCMonth()+n)},(t,n)=>n.getUTCMonth()-t.getUTCMonth()+(n.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth());t6.range;function t7(t){return t5(n=>{n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},(t,n)=>{t.setDate(t.getDate()+7*n)},(t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/6048e5)}let t9=t7(0),nt=t7(1),nn=t7(2),ne=t7(3),nr=t7(4),ni=t7(5),no=t7(6);function nu(t){return t5(n=>{n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+7*n)},(t,n)=>(n-t)/6048e5)}t9.range,nt.range,nn.range,ne.range,nr.range,ni.range,no.range;let na=nu(0),ns=nu(1),nl=nu(2),nc=nu(3),nf=nu(4),nh=nu(5),ng=nu(6);na.range,ns.range,nl.range,nc.range,nf.range,nh.range,ng.range;let nd=t5(t=>t.setHours(0,0,0,0),(t,n)=>t.setDate(t.getDate()+n),(t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1);nd.range;let np=t5(t=>{t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+n)},(t,n)=>(n-t)/864e5,t=>t.getUTCDate()-1);np.range;let ny=t5(t=>{t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+n)},(t,n)=>(n-t)/864e5,t=>Math.floor(t/864e5));ny.range;let nx=t5(t=>{t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds()-6e4*t.getMinutes())},(t,n)=>{t.setTime(+t+36e5*n)},(t,n)=>(n-t)/36e5,t=>t.getHours());nx.range;let nv=t5(t=>{t.setUTCMinutes(0,0,0)},(t,n)=>{t.setTime(+t+36e5*n)},(t,n)=>(n-t)/36e5,t=>t.getUTCHours());nv.range;let n_=t5(t=>{t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds())},(t,n)=>{t.setTime(+t+6e4*n)},(t,n)=>(n-t)/6e4,t=>t.getMinutes());n_.range;let nm=t5(t=>{t.setUTCSeconds(0,0)},(t,n)=>{t.setTime(+t+6e4*n)},(t,n)=>(n-t)/6e4,t=>t.getUTCMinutes());nm.range;let nM=t5(t=>{t.setTime(t-t.getMilliseconds())},(t,n)=>{t.setTime(+t+1e3*n)},(t,n)=>(n-t)/1e3,t=>t.getUTCSeconds());nM.range;let nw=t5(()=>{},(t,n)=>{t.setTime(+t+n)},(t,n)=>n-t);function nb(t,n,e,r,i,o){let u=[[nM,1,1e3],[nM,5,5e3],[nM,15,15e3],[nM,30,3e4],[o,1,6e4],[o,5,3e5],[o,15,9e5],[o,30,18e5],[i,1,36e5],[i,3,108e5],[i,6,216e5],[i,12,432e5],[r,1,864e5],[r,2,1728e5],[e,1,6048e5],[n,1,2592e6],[n,3,7776e6],[t,1,31536e6]];function a(n,e,r){let i=Math.abs(e-n)/r,o=v(([,,t])=>t).right(u,i);if(o===u.length)return t.every(p(n/31536e6,e/31536e6,r));if(0===o)return nw.every(Math.max(p(n,e,r),1));let[a,s]=u[i/u[o-1][2]isFinite(t=Math.floor(t))&&t>0?t>1?t5(n=>{n.setTime(Math.floor(n/t)*t)},(n,e)=>{n.setTime(+n+e*t)},(n,e)=>(e-n)/t):nw:null,nw.range;let[nT,nN]=nb(t4,t6,na,ny,nv,nm),[nk,n$]=nb(t3,t8,t9,nd,nx,n_);function nC(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function nD(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function nU(t,n,e){return{y:t,m:n,d:e,H:0,M:0,S:0,L:0}}var nE={"-":"",_:" ",0:"0"},nS=/^\s*\d+/,nA=/^%/,nF=/[\\^$*+?|[\]().{}]/g;function nO(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n]))}function nH(t,n,e){var r=nS.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function nY(t,n,e){var r=nS.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function nR(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function nI(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function nj(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function nz(t,n,e){var r=nS.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function nZ(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function nB(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function nW(t,n,e){var r=nS.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function nV(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function nX(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function nQ(t,n,e){var r=nS.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function nJ(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function nG(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function nK(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function n0(t,n,e){var r=nS.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function n1(t,n,e){var r=nS.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function n2(t,n,e){var r=nA.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function n5(t,n,e){var r=nS.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function n3(t,n,e){var r=nS.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function n4(t,n){return nO(t.getDate(),n,2)}function n8(t,n){return nO(t.getHours(),n,2)}function n6(t,n){return nO(t.getHours()%12||12,n,2)}function n7(t,n){return nO(1+nd.count(t3(t),t),n,3)}function n9(t,n){return nO(t.getMilliseconds(),n,3)}function et(t,n){return n9(t,n)+"000"}function en(t,n){return nO(t.getMonth()+1,n,2)}function ee(t,n){return nO(t.getMinutes(),n,2)}function er(t,n){return nO(t.getSeconds(),n,2)}function ei(t){var n=t.getDay();return 0===n?7:n}function eo(t,n){return nO(t9.count(t3(t)-1,t),n,2)}function eu(t){var n=t.getDay();return n>=4||0===n?nr(t):nr.ceil(t)}function ea(t,n){return t=eu(t),nO(nr.count(t3(t),t)+(4===t3(t).getDay()),n,2)}function es(t){return t.getDay()}function el(t,n){return nO(nt.count(t3(t)-1,t),n,2)}function ec(t,n){return nO(t.getFullYear()%100,n,2)}function ef(t,n){return nO((t=eu(t)).getFullYear()%100,n,2)}function eh(t,n){return nO(t.getFullYear()%1e4,n,4)}function eg(t,n){var e=t.getDay();return nO((t=e>=4||0===e?nr(t):nr.ceil(t)).getFullYear()%1e4,n,4)}function ed(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+nO(n/60|0,"0",2)+nO(n%60,"0",2)}function ep(t,n){return nO(t.getUTCDate(),n,2)}function ey(t,n){return nO(t.getUTCHours(),n,2)}function ex(t,n){return nO(t.getUTCHours()%12||12,n,2)}function ev(t,n){return nO(1+np.count(t4(t),t),n,3)}function e_(t,n){return nO(t.getUTCMilliseconds(),n,3)}function em(t,n){return e_(t,n)+"000"}function eM(t,n){return nO(t.getUTCMonth()+1,n,2)}function ew(t,n){return nO(t.getUTCMinutes(),n,2)}function eb(t,n){return nO(t.getUTCSeconds(),n,2)}function eT(t){var n=t.getUTCDay();return 0===n?7:n}function eN(t,n){return nO(na.count(t4(t)-1,t),n,2)}function ek(t){var n=t.getUTCDay();return n>=4||0===n?nf(t):nf.ceil(t)}function e$(t,n){return t=ek(t),nO(nf.count(t4(t),t)+(4===t4(t).getUTCDay()),n,2)}function eC(t){return t.getUTCDay()}function eD(t,n){return nO(ns.count(t4(t)-1,t),n,2)}function eU(t,n){return nO(t.getUTCFullYear()%100,n,2)}function eE(t,n){return nO((t=ek(t)).getUTCFullYear()%100,n,2)}function eS(t,n){return nO(t.getUTCFullYear()%1e4,n,4)}function eA(t,n){var e=t.getUTCDay();return nO((t=e>=4||0===e?nf(t):nf.ceil(t)).getUTCFullYear()%1e4,n,4)}function eF(){return"+0000"}function eO(){return"%"}function eq(t){return+t}function eP(t){return Math.floor(t/1e3)}function eL(t){return new Date(t)}function eH(t){return t instanceof Date?+t:+new Date(+t)}function eY(t,n,e,r,i,o,u,a,s,l){var c=t_(),f=c.invert,h=c.domain,g=l(".%L"),d=l(":%S"),p=l("%I:%M"),y=l("%I %p"),x=l("%a %d"),v=l("%b %d"),_=l("%B"),m=l("%Y");function M(t){return(s(t)=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:eq,s:eP,S:er,u:ei,U:eo,V:ea,w:es,W:el,x:null,X:null,y:ec,Y:eh,Z:ed,"%":eO},m={a:function(t){return u[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return s[t.getUTCMonth()]},B:function(t){return a[t.getUTCMonth()]},c:null,d:ep,e:ep,f:em,g:eE,G:eA,H:ey,I:ex,j:ev,L:e_,m:eM,M:ew,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:eq,s:eP,S:eb,u:eT,U:eN,V:e$,w:eC,W:eD,x:null,X:null,y:eU,Y:eS,Z:eF,"%":eO},M={a:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.w=d.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=x.exec(n.slice(e));return r?(t.m=v.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=p.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return T(t,n,e,r)},d:nX,e:nX,f:n1,g:nZ,G:nz,H:nJ,I:nJ,j:nQ,L:n0,m:nV,M:nG,p:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.p=c.get(r[0].toLowerCase()),e+r[0].length):-1},q:nW,Q:n5,s:n3,S:nK,u:nY,U:nR,V:nI,w:nH,W:nj,x:function(t,n,r){return T(t,e,n,r)},X:function(t,n,e){return T(t,r,n,e)},y:nZ,Y:nz,Z:nB,"%":n2};function w(t,n){return function(e){var r,i,o,u=[],a=-1,s=0,l=t.length;for(e instanceof Date||(e=new Date(+e));++a53)return null;"w"in o||(o.w=1),"Z"in o?(r=(i=(r=nD(nU(o.y,0,1))).getUTCDay())>4||0===i?ns.ceil(r):ns(r),r=np.offset(r,(o.V-1)*7),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(r=(i=(r=nC(nU(o.y,0,1))).getDay())>4||0===i?nt.ceil(r):nt(r),r=nd.offset(r,(o.V-1)*7),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:+("W"in o)),i="Z"in o?nD(nU(o.y,0,1)).getUTCDay():nC(nU(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,nD(o)):nC(o)}}function T(t,n,e,r){for(var i,o,u=0,a=n.length,s=e.length;u=s)return -1;if(37===(i=n.charCodeAt(u++))){if(!(o=M[(i=n.charAt(u++))in nE?n.charAt(u++):i])||(r=o(t,e,r))<0)return -1}else if(i!=e.charCodeAt(r++))return -1}return r}return _.x=w(e,_),_.X=w(r,_),_.c=w(n,_),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",_);return n.toString=function(){return t},n},parse:function(t){var n=b(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=b(t+="",!0);return n.toString=function(){return t},n}}}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]})).format,o.parse,a=o.utcFormat,o.utcParse,t.s(["scaleBand",()=>s.default,"scaleDiverging",0,function t(){var n=tS(ez()(tg));return n.copy=function(){return eI(n,t())},tm.initInterpolator.apply(n,arguments)},"scaleDivergingLog",0,function t(){var n=tY(ez()).domain([.1,1,10]);return n.copy=function(){return eI(n,t()).base(n.base())},tm.initInterpolator.apply(n,arguments)},"scaleDivergingPow",0,eZ,"scaleDivergingSqrt",0,function(){return eZ.apply(null,arguments).exponent(.5)},"scaleDivergingSymlog",0,function t(){var n=tj(ez());return n.copy=function(){return eI(n,t()).constant(n.constant())},tm.initInterpolator.apply(n,arguments)},"scaleIdentity",0,function t(n){var e;function r(t){return null==t||isNaN(t*=1)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,tf),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,tf):[0,1],tS(r)},"scaleImplicit",()=>tz.implicit,"scaleLinear",0,function t(){var n=t_();return n.copy=function(){return tx(n,t())},tm.initRange.apply(n,arguments),tS(n)},"scaleLog",0,function t(){let n=tY(tv()).domain([1,10]);return n.copy=()=>tx(n,t()).base(n.base()),tm.initRange.apply(n,arguments),n},"scaleOrdinal",()=>tz.default,"scalePoint",()=>s.point,"scalePow",0,tX,"scaleQuantile",0,function t(){var n,e=[],r=[],i=[];function o(){var t=0,n=Math.max(1,r.length);for(i=Array(n-1);++t=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,o=Math.floor(i),u=+e(t[o],o,t);return u+(e(t[o+1],o+1,t)-u)*(i-o)}}(e,t/n);return u}function u(t){return null==t||isNaN(t*=1)?n:r[w(i,t)]}return u.invertExtent=function(t){var n=r.indexOf(t);return n<0?[NaN,NaN]:[n>0?i[n-1]:e[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},a.unknown=function(t){return arguments.length&&(n=t),a},a.thresholds=function(){return o.slice()},a.copy=function(){return t().domain([e,r]).range(u).unknown(n)},tm.initRange.apply(tS(a),arguments)},"scaleRadial",0,function t(){var n,e=t_(),r=[0,1],i=!1;function o(t){var r,o=Math.sign(r=e(t))*Math.sqrt(Math.abs(r));return isNaN(o)?n:i?Math.round(o):o}return o.invert=function(t){return e.invert(tQ(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,tf)).map(tQ)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},tm.initRange.apply(o,arguments),tS(o)},"scaleSequential",0,function t(){var n=tS(eR()(tg));return n.copy=function(){return eI(n,t())},tm.initInterpolator.apply(n,arguments)},"scaleSequentialLog",0,function t(){var n=tY(eR()).domain([1,10]);return n.copy=function(){return eI(n,t()).base(n.base())},tm.initInterpolator.apply(n,arguments)},"scaleSequentialPow",0,ej,"scaleSequentialQuantile",0,function t(){var n=[],e=tg;function r(t){if(null!=t&&!isNaN(t*=1))return e((w(n,t,1)-1)/(n.length-1))}return r.domain=function(t){if(!arguments.length)return n.slice();for(let e of(n=[],t))null==e||isNaN(e*=1)||n.push(e);return n.sort(y),r},r.interpolator=function(t){return arguments.length?(e=t,r):e},r.range=function(){return n.map((t,r)=>e(r/(n.length-1)))},r.quantiles=function(t){return Array.from({length:t+1},(e,r)=>(function(t,n){if(!(!(e=(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n*=1)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r*=1)>=r&&(yield r)}}(t,void 0))).length)||isNaN(n*=1))){if(n<=0||e<2)return tG(t);if(n>=1)return tJ(t);var e,r=(e-1)*n,i=Math.floor(r),o=tJ((function t(n,e,r=0,i=1/0,o){if(e=Math.floor(e),r=Math.floor(Math.max(0,r)),i=Math.floor(Math.min(n.length-1,i)),!(r<=e&&e<=i))return n;for(o=void 0===o?tK:function(t=y){if(t===y)return tK;if("function"!=typeof t)throw TypeError("compare is not a function");return(n,e)=>{let r=t(n,e);return r||0===r?r:(0===t(e,e))-(0===t(n,n))}}(o);i>r;){if(i-r>600){let u=i-r+1,a=e-r+1,s=Math.log(u),l=.5*Math.exp(2*s/3),c=.5*Math.sqrt(s*l*(u-l)/u)*(a-u/2<0?-1:1),f=Math.max(r,Math.floor(e-a*l/u+c)),h=Math.min(i,Math.floor(e+(u-a)*l/u+c));t(n,e,f,h,o)}let u=n[e],a=r,s=i;for(t0(n,r,e),o(n[i],u)>0&&t0(n,r,i);ao(n[a],u);)++a;for(;o(n[s],u)>0;)--s}0===o(n[r],u)?t0(n,r,s):t0(n,++s,i),s<=e&&(r=s+1),e<=s&&(i=s-1)}return n})(t,i).subarray(0,i+1));return o+(tG(t.subarray(i+1))-o)*(r-i)}})(n,r/t))},r.copy=function(){return t(e).domain(n)},tm.initInterpolator.apply(r,arguments)},"scaleSequentialSqrt",0,function(){return ej.apply(null,arguments).exponent(.5)},"scaleSequentialSymlog",0,function t(){var n=tj(eR());return n.copy=function(){return eI(n,t()).constant(n.constant())},tm.initInterpolator.apply(n,arguments)},"scaleSqrt",0,function(){return tX.apply(null,arguments).exponent(.5)},"scaleSymlog",0,function t(){var n=tj(tv());return n.copy=function(){return tx(n,t()).constant(n.constant())},tm.initRange.apply(n,arguments)},"scaleThreshold",0,function t(){var n,e=[.5],r=[0,1],i=1;function o(t){return null!=t&&t<=t?r[w(e,t,0,i)]:n}return o.domain=function(t){return arguments.length?(i=Math.min((e=Array.from(t)).length,r.length-1),o):e.slice()},o.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),o):r.slice()},o.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t().domain(e).range(r).unknown(n)},tm.initRange.apply(o,arguments)},"scaleTime",0,function(){return tm.initRange.apply(eY(nk,n$,t3,t8,t9,nd,nx,n_,nM,u).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},"scaleUtc",0,function(){return tm.initRange.apply(eY(nT,nN,t4,t6,na,np,nv,nm,nM,a).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},"tickFormat",0,tE],429061)},62990,t=>{"use strict";Array.prototype.slice,t.s(["default",0,function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}])},867719,517306,610010,516039,9506,261770,t=>{"use strict";var n=t.i(62990),e=t.i(475058);function r(t,n){if((i=t.length)>1)for(var e,r,i,o=1,u=t[n[0]],a=u.length;o=0;)e[n]=n;return e}function o(t,n){return t[n]}function u(t){let n=[];return n.key=t,n}t.s(["stack",0,function(){var t=(0,e.default)([]),a=i,s=r,l=o;function c(e){var r,i,o=Array.from(t.apply(this,arguments),u),c=o.length,f=-1;for(let t of e)for(r=0,++f;r0){for(var e,i,o,u=0,a=t[0].length;u0){for(var e,i=0,o=t[n[0]],u=o.length;i0&&(i=(e=t[n[0]]).length)>0){for(var e,i,o,u=0,a=1;a{!function(e){"use strict";var r,i={precision:20,rounding:4,toExpNeg:-7,toExpPos:21,LN10:"2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286"},o=!0,u="[DecimalError] ",a=u+"Invalid argument: ",s=u+"Exponent out of range: ",l=Math.floor,c=Math.pow,f=/^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,h=l(1286742750677284.5),g={};function d(t,n){var e,r,i,u,a,s,l,c,f=t.constructor,h=f.precision;if(!t.s||!n.s)return n.s||(n=new f(t)),o?T(n,h):n;if(l=t.d,c=n.d,a=t.e,i=n.e,l=l.slice(),u=a-i){for(u<0?(r=l,u=-u,s=c.length):(r=c,i=a,s=l.length),u>(s=(a=Math.ceil(h/7))>s?a+1:s+1)&&(u=s,r.length=1),r.reverse();u--;)r.push(0);r.reverse()}for((s=l.length)-(u=c.length)<0&&(u=s,r=c,c=l,l=r),e=0;u;)e=(l[--u]=l[u]+c[u]+e)/1e7|0,l[u]%=1e7;for(e&&(l.unshift(e),++i),s=l.length;0==l[--s];)l.pop();return n.d=l,n.e=i,o?T(n,h):n}function p(t,n,e){if(t!==~~t||te)throw Error(a+t)}function y(t){var n,e,r,i=t.length-1,o="",u=t[0];if(i>0){for(o+=u,n=1;nt.e^this.s<0?1:-1;for(n=0,e=(r=this.d.length)<(i=t.d.length)?r:i;nt.d[n]^this.s<0?1:-1;return r===i?0:r>i^this.s<0?1:-1},g.decimalPlaces=g.dp=function(){var t=this.d.length-1,n=(t-this.e)*7;if(t=this.d[t])for(;t%10==0;t/=10)n--;return n<0?0:n},g.dividedBy=g.div=function(t){return x(this,new this.constructor(t))},g.dividedToIntegerBy=g.idiv=function(t){var n=this.constructor;return T(x(this,new n(t),0,1),n.precision)},g.equals=g.eq=function(t){return!this.cmp(t)},g.exponent=function(){return _(this)},g.greaterThan=g.gt=function(t){return this.cmp(t)>0},g.greaterThanOrEqualTo=g.gte=function(t){return this.cmp(t)>=0},g.isInteger=g.isint=function(){return this.e>this.d.length-2},g.isNegative=g.isneg=function(){return this.s<0},g.isPositive=g.ispos=function(){return this.s>0},g.isZero=function(){return 0===this.s},g.lessThan=g.lt=function(t){return 0>this.cmp(t)},g.lessThanOrEqualTo=g.lte=function(t){return 1>this.cmp(t)},g.logarithm=g.log=function(t){var n,e=this.constructor,i=e.precision,a=i+5;if(void 0===t)t=new e(10);else if((t=new e(t)).s<1||t.eq(r))throw Error(u+"NaN");if(this.s<1)throw Error(u+(this.s?"NaN":"-Infinity"));return this.eq(r)?new e(0):(o=!1,n=x(w(this,a),w(t,a),a),o=!0,T(n,i))},g.minus=g.sub=function(t){return t=new this.constructor(t),this.s==t.s?N(this,t):d(this,(t.s=-t.s,t))},g.modulo=g.mod=function(t){var n,e=this.constructor,r=e.precision;if(!(t=new e(t)).s)throw Error(u+"NaN");return this.s?(o=!1,n=x(this,t,0,1).times(t),o=!0,this.minus(n)):T(new e(this),r)},g.naturalExponential=g.exp=function(){return v(this)},g.naturalLogarithm=g.ln=function(){return w(this)},g.negated=g.neg=function(){var t=new this.constructor(this);return t.s=-t.s||0,t},g.plus=g.add=function(t){return t=new this.constructor(t),this.s==t.s?d(this,t):N(this,(t.s=-t.s,t))},g.precision=g.sd=function(t){var n,e,r;if(void 0!==t&&!!t!==t&&1!==t&&0!==t)throw Error(a+t);if(n=_(this)+1,e=7*(r=this.d.length-1)+1,r=this.d[r]){for(;r%10==0;r/=10)e--;for(r=this.d[0];r>=10;r/=10)e++}return t&&n>e?n:e},g.squareRoot=g.sqrt=function(){var t,n,e,r,i,a,s,c=this.constructor;if(this.s<1){if(!this.s)return new c(0);throw Error(u+"NaN")}for(t=_(this),o=!1,0==(i=Math.sqrt(+this))||i==1/0?(((n=y(this.d)).length+t)%2==0&&(n+="0"),i=Math.sqrt(n),t=l((t+1)/2)-(t<0||t%2),r=new c(n=i==1/0?"5e"+t:(n=i.toExponential()).slice(0,n.indexOf("e")+1)+t)):r=new c(i.toString()),i=s=(e=c.precision)+3;;)if(r=(a=r).plus(x(this,a,s+2)).times(.5),y(a.d).slice(0,s)===(n=y(r.d)).slice(0,s)){if(n=n.slice(s-3,s+1),i==s&&"4999"==n){if(T(a,e+1,0),a.times(a).eq(this)){r=a;break}}else if("9999"!=n)break;s+=4}return o=!0,T(r,e)},g.times=g.mul=function(t){var n,e,r,i,u,a,s,l,c,f=this.constructor,h=this.d,g=(t=new f(t)).d;if(!this.s||!t.s)return new f(0);for(t.s*=this.s,e=this.e+t.e,(l=h.length)<(c=g.length)&&(u=h,h=g,g=u,a=l,l=c,c=a),u=[],r=a=l+c;r--;)u.push(0);for(r=c;--r>=0;){for(n=0,i=l+r;i>r;)s=u[i]+g[r]*h[i-r-1]+n,u[i--]=s%1e7|0,n=s/1e7|0;u[i]=(u[i]+n)%1e7|0}for(;!u[--a];)u.pop();return n?++e:u.shift(),t.d=u,t.e=e,o?T(t,f.precision):t},g.toDecimalPlaces=g.todp=function(t,n){var e=this,r=e.constructor;return(e=new r(e),void 0===t)?e:(p(t,0,1e9),void 0===n?n=r.rounding:p(n,0,8),T(e,t+_(e)+1,n))},g.toExponential=function(t,n){var e,r=this,i=r.constructor;return void 0===t?e=k(r,!0):(p(t,0,1e9),void 0===n?n=i.rounding:p(n,0,8),e=k(r=T(new i(r),t+1,n),!0,t+1)),e},g.toFixed=function(t,n){var e,r,i=this.constructor;return void 0===t?k(this):(p(t,0,1e9),void 0===n?n=i.rounding:p(n,0,8),e=k((r=T(new i(this),t+_(this)+1,n)).abs(),!1,t+_(r)+1),this.isneg()&&!this.isZero()?"-"+e:e)},g.toInteger=g.toint=function(){var t=this.constructor;return T(new t(this),_(this)+1,t.rounding)},g.toNumber=function(){return+this},g.toPower=g.pow=function(t){var n,e,i,a,s,c,f=this,h=f.constructor,g=+(t=new h(t));if(!t.s)return new h(r);if(!(f=new h(f)).s){if(t.s<1)throw Error(u+"Infinity");return f}if(f.eq(r))return f;if(i=h.precision,t.eq(r))return T(f,i);if(c=(n=t.e)>=(e=t.d.length-1),s=f.s,c){if((e=g<0?-g:g)<=0x1fffffffffffff){for(a=new h(r),n=Math.ceil(i/7+4),o=!1;e%2&&$((a=a.times(f)).d,n),0!==(e=l(e/2));)$((f=f.times(f)).d,n);return o=!0,t.s<0?new h(r).div(a):T(a,i)}}else if(s<0)throw Error(u+"NaN");return s=s<0&&1&t.d[Math.max(n,e)]?-1:1,f.s=1,o=!1,a=t.times(w(f,i+12)),o=!0,(a=v(a)).s=s,a},g.toPrecision=function(t,n){var e,r,i=this,o=i.constructor;return void 0===t?(e=_(i),r=k(i,e<=o.toExpNeg||e>=o.toExpPos)):(p(t,1,1e9),void 0===n?n=o.rounding:p(n,0,8),e=_(i=T(new o(i),t,n)),r=k(i,t<=e||e<=o.toExpNeg,t)),r},g.toSignificantDigits=g.tosd=function(t,n){var e=this.constructor;return void 0===t?(t=e.precision,n=e.rounding):(p(t,1,1e9),void 0===n?n=e.rounding:p(n,0,8)),T(new e(this),t,n)},g.toString=g.valueOf=g.val=g.toJSON=function(){var t=_(this),n=this.constructor;return k(this,t<=n.toExpNeg||t>=n.toExpPos)};var x=function(){function t(t,n){var e,r=0,i=t.length;for(t=t.slice();i--;)e=t[i]*n+r,t[i]=e%1e7|0,r=e/1e7|0;return r&&t.unshift(r),t}function n(t,n,e,r){var i,o;if(e!=r)o=e>r?1:-1;else for(i=o=0;in[i]?1:-1;break}return o}function e(t,n,e){for(var r=0;e--;)t[e]-=r,r=+(t[e]1;)t.shift()}return function(r,i,o,a){var s,l,c,f,h,g,d,p,y,x,v,m,M,w,b,N,k,$,C=r.constructor,D=r.s==i.s?1:-1,U=r.d,E=i.d;if(!r.s)return new C(r);if(!i.s)throw Error(u+"Division by zero");for(c=0,l=r.e-i.e,k=E.length,b=U.length,p=(d=new C(D)).d=[];E[c]==(U[c]||0);)++c;if(E[c]>(U[c]||0)&&--l,(m=null==o?o=C.precision:a?o+(_(r)-_(i))+1:o)<0)return new C(0);if(m=m/7+2|0,c=0,1==k)for(f=0,E=E[0],m++;(c1&&(E=t(E,f),U=t(U,f),k=E.length,b=U.length),w=k,x=(y=U.slice(0,k)).length;x=1e7/2&&++N;do f=0,(s=n(E,y,k,x))<0?(v=y[0],k!=x&&(v=1e7*v+(y[1]||0)),(f=v/N|0)>1?(f>=1e7&&(f=1e7-1),g=(h=t(E,f)).length,x=y.length,1==(s=n(h,y,g,x))&&(f--,e(h,k16)throw Error(s+_(t));if(!t.s)return new g(r);for(null==n?(o=!1,l=d):l=n,a=new g(.03125);t.abs().gte(.1);)t=t.times(a),h+=5;for(l+=Math.log(c(2,h))/Math.LN10*2+5|0,e=i=u=new g(r),g.precision=l;;){if(i=T(i.times(t),l),e=e.times(++f),y((a=u.plus(x(i,e,l))).d).slice(0,l)===y(u.d).slice(0,l)){for(;h--;)u=T(u.times(u),l);return g.precision=d,null==n?(o=!0,T(u,d)):u}u=a}}function _(t){for(var n=7*t.e,e=t.d[0];e>=10;e/=10)n++;return n}function m(t,n,e){if(n>t.LN10.sd())throw o=!0,e&&(t.precision=e),Error(u+"LN10 precision limit exceeded");return T(new t(t.LN10),n)}function M(t){for(var n="";t--;)n+="0";return n}function w(t,n){var e,i,a,s,l,c,f,h,g,d=1,p=t,v=p.d,M=p.constructor,b=M.precision;if(p.s<1)throw Error(u+(p.s?"NaN":"-Infinity"));if(p.eq(r))return new M(0);if(null==n?(o=!1,h=b):h=n,p.eq(10))return null==n&&(o=!0),m(M,h);if(M.precision=h+=10,i=(e=y(v)).charAt(0),!(15e14>Math.abs(s=_(p))))return f=m(M,h+2,b).times(s+""),p=w(new M(i+"."+e.slice(1)),h-10).plus(f),M.precision=b,null==n?(o=!0,T(p,b)):p;for(;i<7&&1!=i||1==i&&e.charAt(1)>3;)i=(e=y((p=p.times(t)).d)).charAt(0),d++;for(s=_(p),i>1?(p=new M("0."+e),s++):p=new M(i+"."+e.slice(1)),c=l=p=x(p.minus(r),p.plus(r),h),g=T(p.times(p),h),a=3;;){if(l=T(l.times(g),h),y((f=c.plus(x(l,new M(a),h))).d).slice(0,h)===y(c.d).slice(0,h))return c=c.times(2),0!==s&&(c=c.plus(m(M,h+2,b).times(s+""))),c=x(c,new M(d),h),M.precision=b,null==n?(o=!0,T(c,b)):c;c=f,a+=2}}function b(t,n){var e,r,i;for((e=n.indexOf("."))>-1&&(n=n.replace(".","")),(r=n.search(/e/i))>0?(e<0&&(e=r),e+=+n.slice(r+1),n=n.substring(0,r)):e<0&&(e=n.length),r=0;48===n.charCodeAt(r);)++r;for(i=n.length;48===n.charCodeAt(i-1);)--i;if(n=n.slice(r,i)){if(i-=r,t.e=l((e=e-r-1)/7),t.d=[],r=(e+1)%7,e<0&&(r+=7),rh||t.e<-h))throw Error(s+e)}else t.s=0,t.e=0,t.d=[0];return t}function T(t,n,e){var r,i,u,a,f,g,d,p,y=t.d;for(a=1,u=y[0];u>=10;u/=10)a++;if((r=n-a)<0)r+=7,i=n,d=y[p=0];else{if((p=Math.ceil((r+1)/7))>=(u=y.length))return t;for(a=1,d=u=y[p];u>=10;u/=10)a++;r%=7,i=r-7+a}if(void 0!==e&&(f=d/(u=c(10,a-i-1))%10|0,g=n<0||void 0!==y[p+1]||d%u,g=e<4?(f||g)&&(0==e||e==(t.s<0?3:2)):f>5||5==f&&(4==e||g||6==e&&(r>0?i>0?d/c(10,a-i):0:y[p-1])%10&1||e==(t.s<0?8:7))),n<1||!y[0])return g?(u=_(t),y.length=1,n=n-u-1,y[0]=c(10,(7-n%7)%7),t.e=l(-n/7)||0):(y.length=1,y[0]=t.e=t.s=0),t;if(0==r?(y.length=p,u=1,p--):(y.length=p+1,u=c(10,7-r),y[p]=i>0?(d/c(10,a-i)%c(10,i)|0)*u:0),g)for(;;)if(0==p){1e7==(y[0]+=u)&&(y[0]=1,++t.e);break}else{if(y[p]+=u,1e7!=y[p])break;y[p--]=0,u=1}for(r=y.length;0===y[--r];)y.pop();if(o&&(t.e>h||t.e<-h))throw Error(s+_(t));return t}function N(t,n){var e,r,i,u,a,s,l,c,f,h,g=t.constructor,d=g.precision;if(!t.s||!n.s)return n.s?n.s=-n.s:n=new g(t),o?T(n,d):n;if(l=t.d,h=n.d,r=n.e,c=t.e,l=l.slice(),a=c-r){for((f=a<0)?(e=l,a=-a,s=h.length):(e=h,r=c,s=l.length),a>(i=Math.max(Math.ceil(d/7),s)+2)&&(a=i,e.length=1),e.reverse(),i=a;i--;)e.push(0);e.reverse()}else{for((f=(i=l.length)<(s=h.length))&&(s=i),i=0;i0;--i)l[s++]=0;for(i=h.length;i>a;){if(l[--i]0?o=o.charAt(0)+"."+o.slice(1)+M(r):u>1&&(o=o.charAt(0)+"."+o.slice(1)),o=o+(i<0?"e":"e+")+i):i<0?(o="0."+M(-i-1)+o,e&&(r=e-u)>0&&(o+=M(r))):i>=u?(o+=M(i+1-u),e&&(r=e-i-1)>0&&(o=o+"."+M(r))):((r=i+1)0&&(i+1===u&&(o+="."),o+=M(r))),t.s<0?"-"+o:o}function $(t,n){if(t.length>n)return t.length=n,!0}function C(t){if(!t||"object"!=typeof t)throw Error(u+"Object expected");var n,e,r,i=["precision",1,1e9,"rounding",0,8,"toExpNeg",-1/0,0,"toExpPos",0,1/0];for(n=0;n=i[n+1]&&r<=i[n+2])this[e]=r;else throw Error(a+e+": "+r);if(void 0!==(r=t[e="LN10"]))if(r==Math.LN10)this[e]=new this(r);else throw Error(a+e+": "+r);return this}if((i=function t(n){var e,r,i;function o(t){if(!(this instanceof o))return new o(t);if(this.constructor=o,t instanceof o){this.s=t.s,this.e=t.e,this.d=(t=t.d)?t.slice():t;return}if("number"==typeof t){if(0*t!=0)throw Error(a+t);if(t>0)this.s=1;else if(t<0)t=-t,this.s=-1;else{this.s=0,this.e=0,this.d=[0];return}if(t===~~t&&t<1e7){this.e=0,this.d=[t];return}return b(this,t.toString())}if("string"!=typeof t)throw Error(a+t);if(45===t.charCodeAt(0)?(t=t.slice(1),this.s=-1):this.s=1,f.test(t))b(this,t);else throw Error(a+t)}if(o.prototype=g,o.ROUND_UP=0,o.ROUND_DOWN=1,o.ROUND_CEIL=2,o.ROUND_FLOOR=3,o.ROUND_HALF_UP=4,o.ROUND_HALF_DOWN=5,o.ROUND_HALF_EVEN=6,o.ROUND_HALF_CEIL=7,o.ROUND_HALF_FLOOR=8,o.clone=t,o.config=o.set=C,void 0===n&&(n={}),n)for(e=0,i=["precision","rounding","toExpNeg","toExpPos","LN10"];etypeof self&&self&&self.self==self?self:Function("return this")()),e.Decimal=i)}(t.e)},48114,t=>{"use strict";function n(t){this._context=t}n.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}},t.s(["default",0,function(t){return new n(t)}])},159843,885532,t=>{"use strict";var n=t.i(62990),e=t.i(475058),r=t.i(48114),i=t.i(216888);function o(t){return t[0]}function u(t){return t[1]}t.s(["x",0,o,"y",0,u],885532),t.s(["default",0,function(t,a){var s=(0,e.default)(!0),l=null,c=r.default,f=null,h=(0,i.withPath)(g);function g(e){var r,i,o,u=(e=(0,n.default)(e)).length,g=!1;for(null==l&&(f=c(o=h())),r=0;r<=u;++r)!(r{"use strict";var n=t.i(159843);t.s(["line",()=>n.default])},999173,t=>{"use strict";var n=t.i(62990),e=t.i(475058),r=t.i(48114),i=t.i(159843),o=t.i(216888),u=t.i(885532);t.s(["area",0,function(t,a,s){var l=null,c=(0,e.default)(!0),f=null,h=r.default,g=null,d=(0,o.withPath)(p);function p(e){var r,i,o,u,p,y=(e=(0,n.default)(e)).length,x=!1,v=Array(y),_=Array(y);for(null==f&&(g=h(p=d())),r=0;r<=y;++r){if(!(r=i;--o)g.point(v[o],_[o]);g.lineEnd(),g.areaEnd()}x&&(v[r]=+t(u,r,e),_[r]=+a(u,r,e),g.point(l?+l(u,r,e):v[r],s?+s(u,r,e):_[r]))}if(p)return g=null,p+""||null}function y(){return(0,i.default)().defined(c).curve(h).context(f)}return t="function"==typeof t?t:void 0===t?u.x:(0,e.default)(+t),a="function"==typeof a?a:void 0===a?(0,e.default)(0):(0,e.default)(+a),s="function"==typeof s?s:void 0===s?u.y:(0,e.default)(+s),p.x=function(n){return arguments.length?(t="function"==typeof n?n:(0,e.default)(+n),l=null,p):t},p.x0=function(n){return arguments.length?(t="function"==typeof n?n:(0,e.default)(+n),p):t},p.x1=function(t){return arguments.length?(l=null==t?null:"function"==typeof t?t:(0,e.default)(+t),p):l},p.y=function(t){return arguments.length?(a="function"==typeof t?t:(0,e.default)(+t),s=null,p):a},p.y0=function(t){return arguments.length?(a="function"==typeof t?t:(0,e.default)(+t),p):a},p.y1=function(t){return arguments.length?(s=null==t?null:"function"==typeof t?t:(0,e.default)(+t),p):s},p.lineX0=p.lineY0=function(){return y().x(t).y(a)},p.lineY1=function(){return y().x(t).y(s)},p.lineX1=function(){return y().x(l).y(a)},p.defined=function(t){return arguments.length?(c="function"==typeof t?t:(0,e.default)(!!t),p):c},p.curve=function(t){return arguments.length?(h=t,null!=f&&(g=h(f)),p):h},p.context=function(t){return arguments.length?(null==t?f=g=null:g=h(f=t),p):f},p}],999173)},810489,t=>{"use strict";t.s(["default",0,function(){}])},677304,t=>{"use strict";function n(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function e(t){this._context=t}e.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:n(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t*=1,e*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:n(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},t.s(["default",0,function(t){return new e(t)},"point",0,n])},910118,593866,t=>{"use strict";var n=t.i(810489),e=t.i(677304);function r(t){this._context=t}function i(t){this._context=t}r.prototype={areaStart:n.default,areaEnd:n.default,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:(0,e.point)(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},t.s(["curveBasisClosed",0,function(t){return new r(t)}],910118),i.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+t)/6,i=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(r,i):this._context.moveTo(r,i);break;case 3:this._point=4;default:(0,e.point)(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},t.s(["curveBasisOpen",0,function(t){return new i(t)}],593866)},600104,t=>{"use strict";var n=t.i(677304);t.s(["curveBasis",()=>n.default])},872200,722914,t=>{"use strict";class n{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}t.s(["curveBumpX",0,function(t){return new n(t,!0)}],872200),t.s(["curveBumpY",0,function(t){return new n(t,!1)}],722914)},821641,t=>{"use strict";var n=t.i(810489);function e(t){this._context=t}e.prototype={areaStart:n.default,areaEnd:n.default,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t*=1,n*=1,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},t.s(["curveLinearClosed",0,function(t){return new e(t)}],821641)},851262,t=>{"use strict";var n=t.i(48114);t.s(["curveLinear",()=>n.default])},363823,992536,226619,381142,700357,163018,t=>{"use strict";function n(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0);return((o<0?-1:1)+(u<0?-1:1))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs((o*i+u*r)/(r+i)))||0}function e(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function r(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function i(t){this._context=t}function o(t){this._context=new u(t)}function u(t){this._context=t}function a(t){this._context=t}function s(t){var n,e,r=t.length-1,i=Array(r),o=Array(r),u=Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(n=0,o[r-1]=(t[r]+i[r-1])/2;n=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}},t.s(["curveStep",0,function(t){return new l(t,.5)}],381142),t.s(["curveStepAfter",0,function(t){return new l(t,1)}],700357),t.s(["curveStepBefore",0,function(t){return new l(t,0)}],163018)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/033clseufop6o.js b/litellm/proxy/_experimental/out/_next/static/chunks/033clseufop6o.js deleted file mode 100644 index 3bcc0cb1811..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/033clseufop6o.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,447566,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ArrowLeftOutlined",0,o],447566)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),s=e.i(271645);let o=s.default.forwardRef((e,o)=>{let{color:l,className:i,children:n}=e;return s.default.createElement("p",{ref:o,className:(0,r.tremorTwMerge)("text-tremor-default",l?(0,a.getColorClassNames)(l,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},n)});o.displayName="Text",e.s(["default",0,o],936325),e.s(["Text",0,o],599724)},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let s=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],o=e=>({_s:e,status:s[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),l=e=>e?6:5,i=(e,t,r,a,s)=>{clearTimeout(a.current);let l=o(e);t(l),r.current=l,s&&s({current:l})};var n=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},h=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},p=(0,c.makeClassName)("Button"),f=({loading:e,iconSize:t,iconPosition:r,Icon:s,needMargin:o,transitionStatus:l})=>{let i=o?r===n.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(u,{className:(0,d.tremorTwMerge)(p("icon"),"animate-spin shrink-0",i,m.default,m[l]),style:{transition:"width 150ms"}}):a.default.createElement(s,{className:(0,d.tremorTwMerge)(p("icon"),"shrink-0",t,i)})},b=a.default.forwardRef((e,s)=>{let{icon:u,iconPosition:m=n.HorizontalPositions.Left,size:b=n.Sizes.SM,color:x,variant:v="primary",disabled:w,loading:C=!1,loadingText:y,children:k,tooltip:N,className:T}=e,M=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),S=C||w,E=void 0!==u||C,R=C&&y,j=!(!k&&!R),P=(0,d.tremorTwMerge)(g[b].height,g[b].width),O="light"!==v?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",L=h(v,x),z=("light"!==v?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:_,getReferenceProps:B}=(0,r.useTooltip)(300),[H,I]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:s,timeout:n,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,h]=(0,a.useState)(()=>o(d?2:l(c))),p=(0,a.useRef)(g),f=(0,a.useRef)(0),[b,x]="object"==typeof n?[n.enter,n.exit]:[n,n],v=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return l(t)}})(p.current._s,u);e&&i(e,h,p,f,m)},[m,u]);return[g,(0,a.useCallback)(a=>{let o=e=>{switch(i(e,h,p,f,m),e){case 1:b>=0&&(f.current=((...e)=>setTimeout(...e))(v,b));break;case 4:x>=0&&(f.current=((...e)=>setTimeout(...e))(v,x));break;case 0:case 3:f.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||o(e+1)},0)}},n=p.current.isEnter;"boolean"!=typeof a&&(a=!n),a?n||o(e?+!r:2):n&&o(t?s?3:4:l(u))},[v,m,e,t,r,s,b,x,u]),v]})({timeout:50});return(0,a.useEffect)(()=>{I(C)},[C]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([s,_.refs.setReference]),className:(0,d.tremorTwMerge)(p("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",O,z.paddingX,z.paddingY,z.fontSize,L.textColor,L.bgColor,L.borderColor,L.hoverBorderColor,S?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(h(v,x).hoverTextColor,h(v,x).hoverBgColor,h(v,x).hoverBorderColor),T),disabled:S},B,M),a.default.createElement(r.default,Object.assign({text:N},_)),E&&m!==n.HorizontalPositions.Right?a.default.createElement(f,{loading:C,iconSize:P,iconPosition:m,Icon:u,transitionStatus:H.status,needMargin:j}):null,R||k?a.default.createElement("span",{className:(0,d.tremorTwMerge)(p("text"),"text-tremor-default whitespace-nowrap")},R?y:k):null,E&&m===n.HorizontalPositions.Right?a.default.createElement(f,{loading:C,iconSize:P,iconPosition:m,Icon:u,transitionStatus:H.status,needMargin:j}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),s=e.i(95779),o=e.i(444755),l=e.i(673706);let i=(0,l.makeClassName)("Card"),n=r.default.forwardRef((e,n)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:n,className:(0,o.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,l.getColorClassNames)(c,s.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});n.displayName="Card",e.s(["Card",0,n],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),s=e.i(673706),o=e.i(271645);let l=o.default.forwardRef((e,l)=>{let{color:i,children:n,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return o.default.createElement("p",Object.assign({ref:l,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",i?(0,s.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),n)});l.displayName="Title",e.s(["Title",0,l],629569)},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),a=e.i(540143),s=e.i(915823),o=e.i(619273),l=class extends s.Subscribable{#e;#t=void 0;#r;#a;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#s()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,o.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,o.hashKey)(t.mutationKey)!==(0,o.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#s(),this.#o(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#s(),this.#o()}mutate(e,t){return this.#a=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#s(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#o(e){a.notifyManager.batch(()=>{if(this.#a&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,a={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#a.onSuccess?.(e.data,t,r,a)}catch(e){Promise.reject(e)}try{this.#a.onSettled?.(e.data,null,t,r,a)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#a.onError?.(e.error,t,r,a)}catch(e){Promise.reject(e)}try{this.#a.onSettled?.(void 0,e.error,t,r,a)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},i=e.i(912598);e.s(["useMutation",0,function(e,r){let s=(0,i.useQueryClient)(r),[n]=t.useState(()=>new l(s,e));t.useEffect(()=>{n.setOptions(e)},[n,e]);let d=t.useSyncExternalStore(t.useCallback(e=>n.subscribe(a.notifyManager.batchCalls(e)),[n]),()=>n.getCurrentResult(),()=>n.getCurrentResult()),c=t.useCallback((e,t)=>{n.mutate(e,t).catch(o.noop)},[n]);if(d.error&&(0,o.shouldThrowError)(n.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["default",0,o],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let o=e<0?"-":"",l=Math.abs(e),i=l,n="";return l>=1e6?(i=l/1e6,n="M"):l>=1e3&&(i=l/1e3,n="K"),`${o}${i.toLocaleString("en-US",s)}${n}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(264042).Row;e.s(["Row",0,t],621192)},988297,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,r],988297)},246349,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,t])},695411,e=>{"use strict";var t=e.i(602869);let r=async e=>{try{let r=await (0,t.modelHubCall)(e);if(r?.data.length>0){let e=r.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,t)=>e.model_group.localeCompare(t.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,r])},555987,e=>{"use strict";var t=e.i(221688),r=e.i(950643);let a=/^(https?:|data:|blob:|\/\/)/i;e.s(["resolveLogoSrc",0,(e,s=t.serverRootPath)=>{if(e){let t;return a.test(e)?e:(t=(0,r.normalizeRootPath)(s),`${t}${e.startsWith("/")?e:`/${e}`}`)}}])},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),o=r.default.forwardRef((e,o)=>{let{children:l,className:i}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},n),l))});o.displayName="Table",e.s(["Table",0,o],269200)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),o=r.default.forwardRef((e,o)=>{let{children:l,className:i}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},n),l))});o.displayName="TableHead",e.s(["TableHead",0,o],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),o=r.default.forwardRef((e,o)=>{let{children:l,className:i}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},n),l))});o.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,o],64848)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),o=r.default.forwardRef((e,o)=>{let{children:l,className:i}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},n),l))});o.displayName="TableBody",e.s(["TableBody",0,o],942232)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),o=r.default.forwardRef((e,o)=>{let{children:l,className:i}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("row"),i)},n),l))});o.displayName="TableRow",e.s(["TableRow",0,o],496020)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),o=r.default.forwardRef((e,o)=>{let{children:l,className:i}=e,n=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",i)},n),l))});o.displayName="TableCell",e.s(["TableCell",0,o],977572)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},797672,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,r],797672)},992619,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(779241),s=e.i(599724),o=e.i(199133),l=e.i(983561),i=e.i(695411);e.s(["default",0,({accessToken:e,value:n,placeholder:d="Select a Model",onChange:c,disabled:u=!1,style:m,className:g,showLabel:h=!0,labelText:p="Select Model"})=>{let[f,b]=(0,r.useState)(n),[x,v]=(0,r.useState)(!1),[w,C]=(0,r.useState)([]),y=(0,r.useRef)(null);return(0,r.useEffect)(()=>{b(n)},[n]),(0,r.useEffect)(()=>{e&&(async()=>{try{let t=await (0,i.fetchAvailableModels)(e);t.length>0&&C(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[h&&(0,t.jsxs)(s.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(l.RobotOutlined,{className:"mr-2"})," ",p]}),(0,t.jsx)(o.Select,{value:f,placeholder:d,onChange:e=>{"custom"===e?(v(!0),b(void 0)):(v(!1),b(e),c&&c(e))},options:[...Array.from(new Set(w.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${g||""}`,disabled:u}),x&&(0,t.jsx)(a.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{y.current&&clearTimeout(y.current),y.current=setTimeout(()=>{b(e),c&&c(e)},500)},disabled:u})]})}])},350967,46757,e=>{"use strict";var t=e.i(290571),r=e.i(444755),a=e.i(673706),s=e.i(271645);let o={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},l={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},i={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},n={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,o,"gridColsLg",0,n,"gridColsMd",0,i,"gridColsSm",0,l],46757);let d=(0,a.makeClassName)("Grid"),c=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=s.default.forwardRef((e,a)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:h,children:p,className:f}=e,b=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),x=c(u,o),v=c(m,l),w=c(g,i),C=c(h,n),y=(0,r.tremorTwMerge)(x,v,w,C);return s.default.createElement("div",Object.assign({ref:a,className:(0,r.tremorTwMerge)(d("root"),"grid",y,f)},b),p)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),s=e.i(135214);let o=(0,r.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:r}=(0,s.default)();return(0,t.useQuery)({queryKey:o.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,a.fetchMCPServers)(r,e),enabled:!!r})}])},699857,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),s=e.i(135214);let o=(0,r.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,s.default)();return(0,t.useQuery)({queryKey:o.list(),queryFn:async()=>await (0,a.fetchMCPToolsets)(e),enabled:!!e})}])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},531516,696609,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(536916),s=e.i(599724),o=e.i(409797),l=e.i(246349),l=l;let i=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,n=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,d=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,c=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let r=e.toLowerCase();if(c.test(r))return"read";if(i.test(r))return"delete";if(d.test(r))return"update";if(n.test(r))return"create";if(t){let e=t.toLowerCase();if(c.test(e))return"read";if(i.test(e))return"delete";if(d.test(e))return"update";if(n.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let r of e)t[u(r.name,r.description)].push(r);return t}let g={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,g,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let h=["read","create","update","delete","unknown"],p={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},f={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},b={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:i,onChange:n,readOnly:d=!1,searchFilter:c=""})=>{let[u,x]=(0,r.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),v=(0,r.useMemo)(()=>m(e),[e]),w=(0,r.useMemo)(()=>new Set(void 0===i?e.map(e=>e.name):i),[i,e]),C=e=>{if(d)return;let t=new Set(w);t.has(e)?t.delete(e):t.add(e),n(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:h.map(e=>{let r,i=v[e];if(0===i.length)return null;if(c){let e=c.toLowerCase();if(!i.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=g[e],h=(r=v[e]).length>0&&r.every(e=>w.has(e.name)),y=(e=>{let t=v[e];if(0===t.length)return!1;let r=t.filter(e=>w.has(e.name)).length;return r>0&&r{x(t=>({...t,[e]:!t[e]}))},children:[k?(0,t.jsx)(l.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(o.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${p[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[i.filter(e=>w.has(e.name)).length,"/",i.length," allowed"]})]}),!d&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(s.Text,{className:"text-xs text-gray-500",children:h?"All on":y?"Partial":"All off"}),(0,t.jsx)(a.Checkbox,{checked:h,indeterminate:y,onChange:t=>((e,t)=>{if(d)return;let r=new Set(w);for(let a of v[e])t?r.add(a.name):r.delete(a.name);n(Array.from(r))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!k&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!k&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:i.filter(e=>!c||e.name.toLowerCase().includes(c.toLowerCase())||(e.description??"").toLowerCase().includes(c.toLowerCase())).map(e=>{let r,o=(r=e.name,w.has(r));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!d?"cursor-pointer":""} ${o?"":"opacity-60"}`,onClick:()=>C(e.name),children:[(0,t.jsx)(a.Checkbox,{checked:o,onChange:()=>C(e.name),disabled:d,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(s.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(s.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${o?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:o?"on":"off"})]},e.name)})})]},e)})})}],531516)},962944,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"}}]},name:"thunderbolt",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ThunderboltOutlined",0,o],962944)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/035e9knuui_xh.js b/litellm/proxy/_experimental/out/_next/static/chunks/035e9knuui_xh.js new file mode 100644 index 00000000000..318832bc8ea --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/035e9knuui_xh.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(829087),o=e.i(480731),a=e.i(444755),l=e.i(673706),i=e.i(95779);let s={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,l.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:b="simple",tooltip:p,size:h=o.Sizes.SM,color:f,className:y}=e,v=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),x=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,l.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(b,f),{tooltipProps:O,getReferenceProps:C}=(0,n.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,l.mergeRefs)([m,O.refs.setReference]),className:(0,a.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",x.bgColor,x.textColor,x.borderColor,x.ringColor,c[b].rounded,c[b].border,c[b].shadow,c[b].ring,s[h].paddingX,s[h].paddingY,y)},C,v),r.default.createElement(n.default,Object.assign({text:p},O)),r.default.createElement(g,{className:(0,a.tremorTwMerge)(u("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),n=e.i(122577),o=e.i(278587),a=e.i(68155),l=e.i(360820),i=e.i(871943),s=e.i(434626),d=e.i(551332),c=e.i(592968),u=e.i(115504),m=e.i(752978);function g({icon:e,onClick:r,className:n,disabled:o,dataTestId:a}){return o?(0,t.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,t.jsx)(m.Icon,{icon:e,size:"sm",onClick:r,className:(0,u.cx)("cursor-pointer",n),"data-testid":a})}let b={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:n.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:o.RefreshIcon,className:"hover:text-green-600"},Up:{icon:l.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:i.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:s.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:n=!1,disabledTooltipText:o,dataTestId:a,variant:l}){let{icon:i,className:s}=b[l];return(0,t.jsx)(c.Tooltip,{title:n?o:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(g,{icon:i,onClick:e,className:s,disabled:n,dataTestId:a})})})}],902555)},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),n=e.i(540143),o=e.i(915823),a=e.i(619273),l=class extends o.Subscribable{#e;#t=void 0;#r;#n;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#o()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,a.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,a.hashKey)(t.mutationKey)!==(0,a.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#o(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#o(),this.#a()}mutate(e,t){return this.#n=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#o(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#a(e){n.notifyManager.batch(()=>{if(this.#n&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#n.onSuccess?.(e.data,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(e.data,null,t,r,n)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#n.onError?.(e.error,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(void 0,e.error,t,r,n)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},i=e.i(912598);e.s(["useMutation",0,function(e,r){let o=(0,i.useQueryClient)(r),[s]=t.useState(()=>new l(o,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let d=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(n.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),c=t.useCallback((e,t)=>{s.mutate(e,t).catch(a.noop)},[s]);if(d.error&&(0,a.shouldThrowError)(s.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var o=e.i(9583),a=r.forwardRef(function(e,a){return r.createElement(o.default,(0,t.default)({},e,{ref:a,icon:n}))});e.s(["ExclamationCircleOutlined",0,a],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(529681),o=e.i(242064),a=e.i(517455),l=e.i(185793),i=e.i(721369),s=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let d=e=>{var{prefixCls:n,className:a,hoverable:l=!0}=e,i=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(o.ConfigContext),c=d("card",n),u=(0,r.default)(`${c}-grid`,a,{[`${c}-grid-hoverable`]:l});return t.createElement("div",Object.assign({},i,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),m=e.i(246422),g=e.i(838378);let b=(0,m.genStyleHooks)("Card",e=>{let t=(0,g.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:r,cardHeadPadding:n,colorBorderSecondary:o,boxShadowTertiary:a,bodyPadding:l,extraColor:i}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:a},[`${t}-head`]:(e=>{let{antCls:t,componentCls:r,headerHeight:n,headerPadding:o,tabsMarginBottom:a}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:n,marginBottom:-1,padding:`0 ${(0,c.unit)(o)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${r}-typography, + > ${r}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:a,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:i,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:l,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:r,cardShadow:n,lineWidth:o}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(o)} 0 0 0 ${r}, + 0 ${(0,c.unit)(o)} 0 0 ${r}, + ${(0,c.unit)(o)} ${(0,c.unit)(o)} 0 0 ${r}, + ${(0,c.unit)(o)} 0 0 0 ${r} inset, + 0 ${(0,c.unit)(o)} 0 0 ${r} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:n}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:r,actionsLiMargin:n,cardActionsIconSize:o,colorBorderSecondary:a,actionsBg:l}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:l,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:n,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${r}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${r}`]:{fontSize:o,lineHeight:(0,c.unit)(e.calc(o).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${o}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:r}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:n}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:r,headerPadding:n,bodyPadding:o}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(n)}`,background:r,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(o)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:r,headerPaddingSM:n,headerHeightSM:o,headerFontSizeSM:a}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:o,padding:`0 ${(0,c.unit)(n)}`,fontSize:a,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:r}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,r;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(r=e.headerPadding)?r:e.paddingLG}});var p=e.i(792812),h=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let f=e=>{let{actionClasses:r,actions:n=[],actionStyle:o}=e;return t.createElement("ul",{className:r,style:o},n.map((e,r)=>{let o=`action-${r}`;return t.createElement("li",{style:{width:`${100/n.length}%`},key:o},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:m,rootClassName:g,style:y,extra:v,headStyle:x={},bodyStyle:O={},title:C,loading:$,bordered:j,variant:k,size:w,type:S,cover:E,actions:N,tabList:P,children:M,activeTabKey:T,defaultActiveTabKey:R,tabBarExtraContent:z,hoverable:B,tabProps:I={},classNames:L,styles:H}=e,D=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:G,card:F}=t.useContext(o.ConfigContext),[A]=(0,p.default)("card",k,j),X=e=>{var t;return(0,r.default)(null==(t=null==F?void 0:F.classNames)?void 0:t[e],null==L?void 0:L[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==F?void 0:F.styles)?void 0:t[e]),null==H?void 0:H[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(M,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[M]),V=W("card",u),[U,Y,_]=b(V),Q=t.createElement(l.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},M),J=void 0!==T,Z=Object.assign(Object.assign({},I),{[J?"activeKey":"defaultActiveKey"]:J?T:R,tabBarExtraContent:z}),ee=(0,a.default)(w),et=ee&&"default"!==ee?ee:"large",er=P?t.createElement(i.default,Object.assign({size:et},Z,{className:`${V}-head-tabs`,onChange:t=>{var r;null==(r=e.onTabChange)||r.call(e,t)},items:P.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(C||v||er){let e=(0,r.default)(`${V}-head`,X("header")),n=(0,r.default)(`${V}-head-title`,X("title")),o=(0,r.default)(`${V}-extra`,X("extra")),a=Object.assign(Object.assign({},x),K("header"));c=t.createElement("div",{className:e,style:a},t.createElement("div",{className:`${V}-head-wrapper`},C&&t.createElement("div",{className:n,style:K("title")},C),v&&t.createElement("div",{className:o,style:K("extra")},v)),er)}let en=(0,r.default)(`${V}-cover`,X("cover")),eo=E?t.createElement("div",{className:en,style:K("cover")},E):null,ea=(0,r.default)(`${V}-body`,X("body")),el=Object.assign(Object.assign({},O),K("body")),ei=t.createElement("div",{className:ea,style:el},$?Q:M),es=(0,r.default)(`${V}-actions`,X("actions")),ed=(null==N?void 0:N.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:N}):null,ec=(0,n.default)(D,["onTabChange"]),eu=(0,r.default)(V,null==F?void 0:F.className,{[`${V}-loading`]:$,[`${V}-bordered`]:"borderless"!==A,[`${V}-hoverable`]:B,[`${V}-contain-grid`]:q,[`${V}-contain-tabs`]:null==P?void 0:P.length,[`${V}-${ee}`]:ee,[`${V}-type-${S}`]:!!S,[`${V}-rtl`]:"rtl"===G},m,g,Y,_),em=Object.assign(Object.assign({},null==F?void 0:F.style),y);return U(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:em}),c,eo,ei,ed))});var v=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};y.Grid=d,y.Meta=e=>{let{prefixCls:n,className:a,avatar:l,title:i,description:s}=e,d=v(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(o.ConfigContext),u=c("card",n),m=(0,r.default)(`${u}-meta`,a),g=l?t.createElement("div",{className:`${u}-meta-avatar`},l):null,b=i?t.createElement("div",{className:`${u}-meta-title`},i):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=b||p?t.createElement("div",{className:`${u}-meta-detail`},b,p):null;return t.createElement("div",Object.assign({},d,{className:m}),g,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(908206),o=e.i(242064),a=e.i(517455),l=e.i(150073);let i={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r},u=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let m=e=>{let{itemPrefixCls:n,component:o,span:a,className:l,style:i,labelStyle:d,contentStyle:c,bordered:u,label:m,content:g,colon:b,type:p,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},d),null==h?void 0:h.label),v=Object.assign(Object.assign({},c),null==h?void 0:h.content);if(u)return t.createElement(o,{colSpan:a,style:i,className:(0,r.default)(l,{[`${n}-item-${p}`]:"label"===p||"content"===p,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===p,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===p})},null!=m&&t.createElement("span",{style:y},m),null!=g&&t.createElement("span",{style:v},g));return t.createElement(o,{colSpan:a,style:i,className:(0,r.default)(`${n}-item`,l)},t.createElement("div",{className:`${n}-item-container`},null!=m&&t.createElement("span",{style:y,className:(0,r.default)(`${n}-item-label`,null==f?void 0:f.label,{[`${n}-item-no-colon`]:!b})},m),null!=g&&t.createElement("span",{style:v,className:(0,r.default)(`${n}-item-content`,null==f?void 0:f.content)},g)))};function g(e,{colon:r,prefixCls:n,bordered:o},{component:a,type:l,showLabel:i,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:g,prefixCls:b=n,className:p,style:h,labelStyle:f,contentStyle:y,span:v=1,key:x,styles:O},C)=>"string"==typeof a?t.createElement(m,{key:`${l}-${x||C}`,className:p,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),null==O?void 0:O.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),y),null==O?void 0:O.content)},span:v,colon:r,component:a,itemPrefixCls:b,bordered:o,label:i?e:null,content:s?g:null,type:l}):[t.createElement(m,{key:`label-${x||C}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),f),null==O?void 0:O.label),span:1,colon:r,component:a[0],itemPrefixCls:b,bordered:o,label:e,type:"label"}),t.createElement(m,{key:`content-${x||C}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),h),y),null==O?void 0:O.content),span:2*v-1,component:a[1],itemPrefixCls:b,bordered:o,content:g,type:"content"})])}let b=e=>{let r=t.useContext(s),{prefixCls:n,vertical:o,row:a,index:l,bordered:i}=e;return o?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${l}`,className:`${n}-row`},g(a,e,Object.assign({component:"th",type:"label",showLabel:!0},r))),t.createElement("tr",{key:`content-${l}`,className:`${n}-row`},g(a,e,Object.assign({component:"td",type:"content",showContent:!0},r)))):t.createElement("tr",{key:l,className:`${n}-row`},g(a,e,Object.assign({component:i?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},r)))};e.i(296059);var p=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let v=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:r,itemPaddingBottom:n,itemPaddingEnd:o,colonMarginRight:a,colonMarginLeft:l,titleMarginBottom:i}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:r}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:r,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:i},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:r,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:n,paddingInlineEnd:o},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(l)} ${(0,p.unit)(a)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var x=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let O=e=>{let m,{prefixCls:g,title:p,extra:h,column:f,colon:y=!0,bordered:O,layout:C,children:$,className:j,rootClassName:k,style:w,size:S,labelStyle:E,contentStyle:N,styles:P,items:M,classNames:T}=e,R=x(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:z,direction:B,className:I,style:L,classNames:H,styles:D}=(0,o.useComponentConfig)("descriptions"),W=z("descriptions",g),G=(0,l.default)(),F=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,n.matchScreen)(G,Object.assign(Object.assign({},i),f)))?e:3},[G,f]),A=(m=t.useMemo(()=>M||(0,d.default)($).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[M,$]),t.useMemo(()=>m.map(e=>{var{span:t}=e,r=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},r),{filled:!0}):Object.assign(Object.assign({},r),{span:"number"==typeof t?t:(0,n.matchScreen)(G,t)})}),[m,G])),X=(0,a.default)(S),K=((e,r)=>{let[n,o]=(0,t.useMemo)(()=>{let t,n,o,a;return t=[],n=[],o=!1,a=0,r.filter(e=>e).forEach(r=>{let{filled:l}=r,i=u(r,["filled"]);if(l){n.push(i),t.push(n),n=[],a=0;return}let s=e-a;(a+=r.span||1)>=e?(a>e?(o=!0,n.push(Object.assign(Object.assign({},i),{span:s}))):n.push(i),t.push(n),n=[],a=0):n.push(i)}),n.length>0&&t.push(n),[t=t.map(t=>{let r=t.reduce((e,t)=>e+(t.span||1),0);if(r({labelStyle:E,contentStyle:N,styles:{content:Object.assign(Object.assign({},D.content),null==P?void 0:P.content),label:Object.assign(Object.assign({},D.label),null==P?void 0:P.label)},classNames:{label:(0,r.default)(H.label,null==T?void 0:T.label),content:(0,r.default)(H.content,null==T?void 0:T.content)}}),[E,N,P,T,H,D]);return q(t.createElement(s.Provider,{value:Y},t.createElement("div",Object.assign({className:(0,r.default)(W,I,H.root,null==T?void 0:T.root,{[`${W}-${X}`]:X&&"default"!==X,[`${W}-bordered`]:!!O,[`${W}-rtl`]:"rtl"===B},j,k,V,U),style:Object.assign(Object.assign(Object.assign(Object.assign({},L),D.root),null==P?void 0:P.root),w)},R),(p||h)&&t.createElement("div",{className:(0,r.default)(`${W}-header`,H.header,null==T?void 0:T.header),style:Object.assign(Object.assign({},D.header),null==P?void 0:P.header)},p&&t.createElement("div",{className:(0,r.default)(`${W}-title`,H.title,null==T?void 0:T.title),style:Object.assign(Object.assign({},D.title),null==P?void 0:P.title)},p),h&&t.createElement("div",{className:(0,r.default)(`${W}-extra`,H.extra,null==T?void 0:T.extra),style:Object.assign(Object.assign({},D.extra),null==P?void 0:P.extra)},h)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,r)=>t.createElement(b,{key:r,index:r,colon:y,prefixCls:W,vertical:"vertical"===C,bordered:O,row:e}))))))))};O.Item=({children:e})=>e,e.s(["Descriptions",0,O],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),r=e.i(732961),n=e.i(289882),o=e.i(170517),a=e.i(628882),l=e.i(320890),i=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),m=e.i(328052),g=e.i(135551);let b=(e,t)=>new g.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new g.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let r=e||"#000",n=t||"#fff";return{colorBgBase:r,colorTextBase:n,colorText:b(n,.85),colorTextSecondary:b(n,.65),colorTextTertiary:b(n,.45),colorTextQuaternary:b(n,.25),colorFill:b(n,.18),colorFillSecondary:b(n,.12),colorFillTertiary:b(n,.08),colorFillQuaternary:b(n,.04),colorBgSolid:b(n,.95),colorBgSolidHover:b(n,1),colorBgSolidActive:b(n,.9),colorBgElevated:p(r,12),colorBgContainer:p(r,8),colorBgLayout:p(r,0),colorBgSpotlight:p(r,26),colorBgBlur:b(n,.04),colorBorder:p(r,26),colorBorderSecondary:p(r,19)}},y={defaultSeed:l.defaultConfig.token,useToken:function(){let[e,t,r]=(0,i.useToken)();return{theme:e,token:t,hashId:r}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let r=Object.keys(o.defaultPresetColors).map(t=>{let r=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,n,o)=>(e[`${t}-${o+1}`]=r[o],e[`${t}${o+1}`]=r[o],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),n=null!=t?t:(0,s.default)(e),a=(0,m.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},n),r),a),{colorPrimaryBg:a.colorPrimaryBorder,colorPrimaryBgHover:a.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let r=null!=t?t:(0,s.default)(e),n=r.fontSizeSM,o=r.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},r),function(e){let{sizeUnit:t,sizeStep:r}=e,n=r-2;return{sizeXXL:t*(n+10),sizeXL:t*(n+6),sizeLG:t*(n+2),sizeMD:t*(n+2),sizeMS:t*(n+1),size:t*n,sizeSM:t*n,sizeXS:t*(n-1),sizeXXS:t*(n-1)}}(null!=t?t:e)),(0,c.default)(n)),{controlHeight:o}),(0,d.default)(Object.assign(Object.assign({},r),{controlHeight:o})))},getDesignToken:e=>{let l=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):n.default,i=Object.assign(Object.assign({},o.default),null==e?void 0:e.token);return(0,r.getComputedToken)(i,{override:null==e?void 0:e.token},l,a.default)},defaultConfig:l.defaultConfig,_internalContext:l.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),r=e.i(560445),n=e.i(175712),o=e.i(869216),a=e.i(311451),l=e.i(212931),i=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:m,message:g,resourceInformationTitle:b,resourceInformation:p,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:v}){let{Title:x,Text:O}=i.Typography,{token:C}=s.theme.useToken(),[$,j]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&j("")},[e]),(0,t.jsx)(l.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!v&&$!==v||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[m&&(0,t.jsx)(r.Alert,{message:m,type:"warning"}),(0,t.jsx)(n.Card,{title:b,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder}},style:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder},children:(0,t.jsx)(o.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:r,...n})=>(0,t.jsx)(o.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(O,{...n,children:r??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(O,{children:g})}),v&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(O,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(O,{children:"Type "}),(0,t.jsx)(O,{strong:!0,type:"danger",children:v}),(0,t.jsx)(O,{children:" to confirm deletion:"})]}),(0,t.jsx)(a.Input,{value:$,onChange:e=>j(e.target.value),placeholder:v,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:C.colorError}}),autoFocus:!0})]})]})})}])},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},727612,e=>{"use strict";let t=(0,e.i(475254).default)("trash-2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);e.s(["Trash2",0,t],727612)},823429,e=>{"use strict";let t=(0,e.i(475254).default)("square-pen",[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]]);e.s(["default",0,t])},888288,e=>{"use strict";var t=e.i(271645);e.s(["default",0,(e,r)=>{let n=void 0!==r,[o,a]=(0,t.useState)(e);return[n?r:o,e=>{n||a(e)}]}])},793130,e=>{"use strict";var t=e.i(290571),r=e.i(783222),n=e.i(433336),o=e.i(271645),a=e.i(394487),l=e.i(503269),i=e.i(214520),s=e.i(746725),d=e.i(914189),c=e.i(144279),u=e.i(294316),m=e.i(601893),g=e.i(140721),b=e.i(942803),p=e.i(233538),h=e.i(694421),f=e.i(700020),y=e.i(35889),v=e.i(998348),x=e.i(722678);let O=(0,o.createContext)(null);O.displayName="GroupContext";let C=o.Fragment,$=Object.assign((0,f.forwardRefWithAs)(function(e,t){var C;let $=(0,o.useId)(),j=(0,b.useProvidedId)(),k=(0,m.useDisabled)(),{id:w=j||`headlessui-switch-${$}`,disabled:S=k||!1,checked:E,defaultChecked:N,onChange:P,name:M,value:T,form:R,autoFocus:z=!1,...B}=e,I=(0,o.useContext)(O),[L,H]=(0,o.useState)(null),D=(0,o.useRef)(null),W=(0,u.useSyncRefs)(D,t,null===I?null:I.setSwitch,H),G=(0,i.useDefaultValue)(N),[F,A]=(0,l.useControllable)(E,P,null!=G&&G),X=(0,s.useDisposables)(),[K,q]=(0,o.useState)(!1),V=(0,d.useEvent)(()=>{q(!0),null==A||A(!F),X.nextFrame(()=>{q(!1)})}),U=(0,d.useEvent)(e=>{if((0,p.isDisabledReactIssue7711)(e.currentTarget))return e.preventDefault();e.preventDefault(),V()}),Y=(0,d.useEvent)(e=>{e.key===v.Keys.Space?(e.preventDefault(),V()):e.key===v.Keys.Enter&&(0,h.attemptSubmit)(e.currentTarget)}),_=(0,d.useEvent)(e=>e.preventDefault()),Q=(0,x.useLabelledBy)(),J=(0,y.useDescribedBy)(),{isFocusVisible:Z,focusProps:ee}=(0,r.useFocusRing)({autoFocus:z}),{isHovered:et,hoverProps:er}=(0,n.useHover)({isDisabled:S}),{pressed:en,pressProps:eo}=(0,a.useActivePress)({disabled:S}),ea=(0,o.useMemo)(()=>({checked:F,disabled:S,hover:et,focus:Z,active:en,autofocus:z,changing:K}),[F,et,Z,en,S,K,z]),el=(0,f.mergeProps)({id:w,ref:W,role:"switch",type:(0,c.useResolveButtonType)(e,L),tabIndex:-1===e.tabIndex?0:null!=(C=e.tabIndex)?C:0,"aria-checked":F,"aria-labelledby":Q,"aria-describedby":J,disabled:S||void 0,autoFocus:z,onClick:U,onKeyUp:Y,onKeyPress:_},ee,er,eo),ei=(0,o.useCallback)(()=>{if(void 0!==G)return null==A?void 0:A(G)},[A,G]),es=(0,f.useRender)();return o.default.createElement(o.default.Fragment,null,null!=M&&o.default.createElement(g.FormFields,{disabled:S,data:{[M]:T||"on"},overrides:{type:"checkbox",checked:F},form:R,onReset:ei}),es({ourProps:el,theirProps:B,slot:ea,defaultTag:"button",name:"Switch"}))}),{Group:function(e){var t;let[r,n]=(0,o.useState)(null),[a,l]=(0,x.useLabels)(),[i,s]=(0,y.useDescriptions)(),d=(0,o.useMemo)(()=>({switch:r,setSwitch:n}),[r,n]),c=(0,f.useRender)();return o.default.createElement(s,{name:"Switch.Description",value:i},o.default.createElement(l,{name:"Switch.Label",value:a,props:{htmlFor:null==(t=d.switch)?void 0:t.id,onClick(e){r&&(e.currentTarget instanceof HTMLLabelElement&&e.preventDefault(),r.click(),r.focus({preventScroll:!0}))}}},o.default.createElement(O.Provider,{value:d},c({ourProps:{},theirProps:e,slot:{},defaultTag:C,name:"Switch.Group"}))))},Label:x.Label,Description:y.Description});var j=e.i(888288),k=e.i(95779),w=e.i(444755),S=e.i(673706),E=e.i(829087);let N=(0,S.makeClassName)("Switch"),P=o.default.forwardRef((e,r)=>{let{checked:n,defaultChecked:a=!1,onChange:l,color:i,name:s,error:d,errorMessage:c,disabled:u,required:m,tooltip:g,id:b}=e,p=(0,t.__rest)(e,["checked","defaultChecked","onChange","color","name","error","errorMessage","disabled","required","tooltip","id"]),h={bgColor:i?(0,S.getColorClassNames)(i,k.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",ringColor:i?(0,S.getColorClassNames)(i,k.colorPalette.ring).ringColor:"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"},[f,y]=(0,j.default)(a,n),[v,x]=(0,o.useState)(!1),{tooltipProps:O,getReferenceProps:C}=(0,E.useTooltip)(300);return o.default.createElement("div",{className:"flex flex-row items-center justify-start"},o.default.createElement(E.default,Object.assign({text:g},O)),o.default.createElement("div",Object.assign({ref:(0,S.mergeRefs)([r,O.refs.setReference]),className:(0,w.tremorTwMerge)(N("root"),"flex flex-row relative h-5")},p,C),o.default.createElement("input",{type:"checkbox",className:(0,w.tremorTwMerge)(N("input"),"absolute w-5 h-5 cursor-pointer left-0 top-0 opacity-0"),name:s,required:m,checked:f,onChange:e=>{e.preventDefault()}}),o.default.createElement($,{checked:f,onChange:e=>{y(e),null==l||l(e)},disabled:u,className:(0,w.tremorTwMerge)(N("switch"),"w-10 h-5 group relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-tremor-full","focus:outline-none",u?"cursor-not-allowed":""),onFocus:()=>x(!0),onBlur:()=>x(!1),id:b},o.default.createElement("span",{className:(0,w.tremorTwMerge)(N("sr-only"),"sr-only")},"Switch ",f?"on":"off"),o.default.createElement("span",{"aria-hidden":"true",className:(0,w.tremorTwMerge)(N("background"),f?h.bgColor:"bg-tremor-border dark:bg-dark-tremor-border","pointer-events-none absolute mx-auto h-3 w-9 rounded-tremor-full transition-colors duration-100 ease-in-out")}),o.default.createElement("span",{"aria-hidden":"true",className:(0,w.tremorTwMerge)(N("round"),f?(0,w.tremorTwMerge)(h.bgColor,"translate-x-5 border-tremor-background dark:border-dark-tremor-background"):"translate-x-0 bg-tremor-border dark:bg-dark-tremor-border border-tremor-background dark:border-dark-tremor-background","pointer-events-none absolute left-0 inline-block h-5 w-5 transform rounded-tremor-full border-2 shadow-tremor-input duration-100 ease-in-out transition",v?(0,w.tremorTwMerge)("ring-2",h.ringColor):"")}))),d&&c?o.default.createElement("p",{className:(0,w.tremorTwMerge)(N("errorMessage"),"text-sm text-red-500 mt-1 ")},c):null)});P.displayName="Switch",e.s(["Switch",0,P],793130)},688511,e=>{"use strict";var t=e.i(823429);e.s(["Edit",()=>t.default])},269638,e=>{"use strict";let t=(0,e.i(475254).default)("circle-check-big",[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]]);e.s(["CheckCircle",0,t],269638)},431343,569074,e=>{"use strict";var t=e.i(475254);let r=(0,t.default)("play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);e.s(["Play",0,r],431343);let n=(0,t.default)("upload",[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]]);e.s(["Upload",0,n],569074)},883552,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(562901),n=e.i(343794),o=e.i(914949),a=e.i(529681),l=e.i(242064),i=e.i(829672),s=e.i(285781),d=e.i(836938),c=e.i(920228),u=e.i(62405),m=e.i(408850),g=e.i(87414),b=e.i(310730);let p=(0,e.i(246422).genStyleHooks)("Popconfirm",e=>(e=>{let{componentCls:t,iconCls:r,antCls:n,zIndexPopup:o,colorText:a,colorWarning:l,marginXXS:i,marginXS:s,fontSize:d,fontWeightStrong:c,colorTextHeading:u}=e;return{[t]:{zIndex:o,[`&${n}-popover`]:{fontSize:d},[`${t}-message`]:{marginBottom:s,display:"flex",flexWrap:"nowrap",alignItems:"start",[`> ${t}-message-icon ${r}`]:{color:l,fontSize:d,lineHeight:1,marginInlineEnd:s},[`${t}-title`]:{fontWeight:c,color:u,"&:only-child":{fontWeight:"normal"}},[`${t}-description`]:{marginTop:i,color:a}},[`${t}-buttons`]:{textAlign:"end",whiteSpace:"nowrap",button:{marginInlineStart:s}}}}})(e),e=>{let{zIndexPopupBase:t}=e;return{zIndexPopup:t+60}},{resetStyle:!1});var h=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let f=e=>{let{prefixCls:n,okButtonProps:o,cancelButtonProps:a,title:i,description:b,cancelText:p,okText:h,okType:f="primary",icon:y=t.createElement(r.default,null),showCancel:v=!0,close:x,onConfirm:O,onCancel:C,onPopupClick:$}=e,{getPrefixCls:j}=t.useContext(l.ConfigContext),[k]=(0,m.useLocale)("Popconfirm",g.default.Popconfirm),w=(0,d.getRenderPropValue)(i),S=(0,d.getRenderPropValue)(b);return t.createElement("div",{className:`${n}-inner-content`,onClick:$},t.createElement("div",{className:`${n}-message`},y&&t.createElement("span",{className:`${n}-message-icon`},y),t.createElement("div",{className:`${n}-message-text`},w&&t.createElement("div",{className:`${n}-title`},w),S&&t.createElement("div",{className:`${n}-description`},S))),t.createElement("div",{className:`${n}-buttons`},v&&t.createElement(c.default,Object.assign({onClick:C,size:"small"},a),p||(null==k?void 0:k.cancelText)),t.createElement(s.default,{buttonProps:Object.assign(Object.assign({size:"small"},(0,u.convertLegacyProps)(f)),o),actionFn:O,close:x,prefixCls:j("btn"),quitOnNullishReturnValue:!0,emitEvent:!0},h||(null==k?void 0:k.okText))))};var y=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let v=t.forwardRef((e,s)=>{var d,c;let{prefixCls:u,placement:m="top",trigger:g="click",okType:b="primary",icon:h=t.createElement(r.default,null),children:v,overlayClassName:x,onOpenChange:O,onVisibleChange:C,overlayStyle:$,styles:j,classNames:k}=e,w=y(e,["prefixCls","placement","trigger","okType","icon","children","overlayClassName","onOpenChange","onVisibleChange","overlayStyle","styles","classNames"]),{getPrefixCls:S,className:E,style:N,classNames:P,styles:M}=(0,l.useComponentConfig)("popconfirm"),[T,R]=(0,o.default)(!1,{value:null!=(d=e.open)?d:e.visible,defaultValue:null!=(c=e.defaultOpen)?c:e.defaultVisible}),z=(e,t)=>{R(e,!0),null==C||C(e),null==O||O(e,t)},B=S("popconfirm",u),I=(0,n.default)(B,E,x,P.root,null==k?void 0:k.root),L=(0,n.default)(P.body,null==k?void 0:k.body),[H]=p(B);return H(t.createElement(i.default,Object.assign({},(0,a.default)(w,["title"]),{trigger:g,placement:m,onOpenChange:(t,r)=>{let{disabled:n=!1}=e;n||z(t,r)},open:T,ref:s,classNames:{root:I,body:L},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},M.root),N),$),null==j?void 0:j.root),body:Object.assign(Object.assign({},M.body),null==j?void 0:j.body)},content:t.createElement(f,Object.assign({okType:b,icon:h},e,{prefixCls:B,close:e=>{z(!1,e)},onConfirm:t=>{var r;return null==(r=e.onConfirm)?void 0:r.call(void 0,t)},onCancel:t=>{var r;z(!1,t),null==(r=e.onCancel)||r.call(void 0,t)}})),"data-popover-inject":!0}),v))});v._InternalPanelDoNotUseOrYouWillBeFired=e=>{let{prefixCls:r,placement:o,className:a,style:i}=e,s=h(e,["prefixCls","placement","className","style"]),{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("popconfirm",r),[u]=p(c);return u(t.createElement(b.default,{placement:o,className:(0,n.default)(c,a),style:i,content:t.createElement(f,Object.assign({prefixCls:c},s))}))},e.s(["Popconfirm",0,v],883552)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/035i-tbvd3z7s.js b/litellm/proxy/_experimental/out/_next/static/chunks/035i-tbvd3z7s.js deleted file mode 100644 index 7e8cf73121d..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/035i-tbvd3z7s.js +++ /dev/null @@ -1,10 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,664307,e=>{"use strict";var t=e.i(843476),l=e.i(602869),s=e.i(266027),a=e.i(243652),r=e.i(135214);let i=(0,a.createQueryKeys)("credentials"),o=()=>{let{accessToken:e}=(0,r.default)();return(0,s.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,l.credentialListCall)(e),enabled:!!e})};var n=e.i(368670),d=e.i(625901),c=e.i(292639),m=e.i(954616),u=e.i(785242),h=e.i(152990),p=e.i(682830),x=e.i(271645),g=e.i(269200),f=e.i(427612),_=e.i(64848),j=e.i(942232),y=e.i(496020),b=e.i(977572),v=e.i(446891);function N({data:e=[],columns:l,isLoading:s=!1,sorting:a=[],onSortingChange:r,pagination:i,onPaginationChange:o,enablePagination:n=!1,onRowClick:d}){let[c]=x.default.useState("onChange"),[m,u]=x.default.useState({}),[w,C]=x.default.useState({}),k=(0,h.useReactTable)({data:e,columns:l,state:{sorting:a,columnSizing:m,columnVisibility:w,...n&&i?{pagination:i}:{}},columnResizeMode:c,onSortingChange:r,onColumnSizingChange:u,onColumnVisibilityChange:C,...n&&o?{onPaginationChange:o}:{},getCoreRowModel:(0,p.getCoreRowModel)(),...n?{getPaginationRowModel:(0,p.getPaginationRowModel)()}:{},enableSorting:!0,enableColumnResizing:!0,manualSorting:!0,defaultColumn:{minSize:40,maxSize:500}});return(0,t.jsx)("div",{className:"rounded-lg custom-border relative",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsx)("div",{className:"relative min-w-full",children:(0,t.jsxs)(g.Table,{className:"[&_td]:py-2 [&_th]:py-2",style:{width:k.getTotalSize(),minWidth:"100%",tableLayout:"fixed"},children:[(0,t.jsx)(f.TableHead,{children:k.getHeaderGroups().map(e=>(0,t.jsx)(y.TableRow,{children:e.headers.map(e=>(0,t.jsxs)(_.TableHeaderCell,{className:`py-1 h-8 relative ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8":""} ${e.column.columnDef.meta?.className||""}`,style:{width:"actions"===e.id?120:e.getSize(),position:"actions"===e.id?"sticky":"relative",right:"actions"===e.id?0:"auto"},children:[(0,t.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,t.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,h.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&e.column.getCanSort()&&r&&(0,t.jsx)(v.TableHeaderSortDropdown,{sortState:!1!==e.column.getIsSorted()&&e.column.getIsSorted(),onSortChange:t=>{!1===t?r([]):r([{id:e.column.id,desc:"desc"===t}])},columnId:e.column.id})]}),e.column.getCanResize()&&(0,t.jsx)("div",{onMouseDown:e.getResizeHandler(),onTouchStart:e.getResizeHandler(),className:`absolute right-0 top-0 h-full w-2 cursor-col-resize select-none touch-none ${e.column.getIsResizing()?"bg-blue-500":"hover:bg-blue-200"}`})]},e.id))},e.id))}),(0,t.jsx)(j.TableBody,{children:s?(0,t.jsx)(y.TableRow,{children:(0,t.jsx)(b.TableCell,{colSpan:l.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"🚅 Loading models..."})})})}):k.getRowModel().rows.length>0?k.getRowModel().rows.map(e=>(0,t.jsx)(y.TableRow,{className:d?"cursor-pointer hover:bg-gray-50":"",onClick:()=>d?.(e.original),children:e.getVisibleCells().map(e=>(0,t.jsx)(b.TableCell,{className:`py-0.5 overflow-hidden ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8":""} ${e.column.columnDef.meta?.className||""}`,style:{width:"actions"===e.column.id?120:e.column.getSize(),position:"actions"===e.column.id?"sticky":"relative",right:"actions"===e.column.id?0:"auto"},children:(0,h.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,t.jsx)(y.TableRow,{children:(0,t.jsx)(b.TableCell,{colSpan:l.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"No models found"})})})})})]})})})})}var w=e.i(751904),C=e.i(827252),k=e.i(772345),S=e.i(68155),T=e.i(389083),I=e.i(994388),F=e.i(752978),P=e.i(312361),M=e.i(525720),A=e.i(282786),E=e.i(770914),L=e.i(790848),O=e.i(592968),R=e.i(898586),B=e.i(418371);let{Text:z,Title:q}=R.Typography,V=(0,t.jsxs)(E.Space,{direction:"vertical",size:12,children:[(0,t.jsx)(z,{strong:!0,style:{fontSize:13},children:"Credential types"}),(0,t.jsxs)(E.Space,{direction:"vertical",size:8,children:[(0,t.jsx)(M.Flex,{align:"center",gap:8,children:(0,t.jsxs)(E.Space,{direction:"vertical",children:[(0,t.jsxs)(M.Flex,{align:"center",gap:8,children:[(0,t.jsx)(k.SyncOutlined,{style:{color:"#1890ff"}}),(0,t.jsx)(q,{level:5,style:{margin:0,color:"#1890ff"},children:"Reusable"})]}),(0,t.jsx)(z,{type:"secondary",children:"Credentials saved in LiteLLM that can be added to models repeatedly."})]})}),(0,t.jsx)(P.Divider,{size:"small"}),(0,t.jsx)(M.Flex,{align:"center",gap:8,children:(0,t.jsxs)(E.Space,{direction:"vertical",size:8,children:[(0,t.jsxs)(M.Flex,{align:"center",gap:8,children:[(0,t.jsx)(w.EditOutlined,{style:{color:"#8c8c8c",fontSize:14,flexShrink:0}}),(0,t.jsx)(q,{level:5,style:{margin:0},children:"Manual"})]}),(0,t.jsx)(z,{type:"secondary",children:"Credentials added directly during model creation or defined in the config file."})]})})]})]}),D=e=>e?.model_info?.team_public_model_name?e.model_info.team_public_model_name:e?.model_name||"-";var H=e.i(127952),G=e.i(727749),U=e.i(313603),$=e.i(912598),K=e.i(350967),J=e.i(404206),W=e.i(906579),Q=e.i(464571),Y=e.i(199133),X=e.i(981339),Z=e.i(153472);let ee=async(e,t)=>{let s=(0,l.getProxyBaseUrl)(),a=s?`${s}/config/field/update`:"/config/field/update",r=await fetch(a,{method:"POST",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({field_name:"store_model_in_db",field_value:t.store_model_in_db,config_type:"general_settings"})});if(!r.ok){let e=await r.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to update model storage settings")}return await r.json()};var et=e.i(190702),el=e.i(808613),es=e.i(212931);let ea=({isVisible:e,onCancel:l,onSuccess:s})=>{let[a]=el.Form.useForm(),{mutateAsync:i,isPending:o}=(()=>{let{accessToken:e}=(0,r.default)();return(0,m.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return await ee(e,t)}})})(),{data:n,isLoading:d,refetch:c}=(0,Z.useProxyConfig)(Z.ConfigType.GENERAL_SETTINGS);(0,x.useEffect)(()=>{e&&c()},[e,c]);let u=(0,x.useMemo)(()=>{if(!n)return{store_model_in_db:!1};let e=n.find(e=>"store_model_in_db"===e.field_name);return{store_model_in_db:e?.field_value??!1}},[n]),h=async e=>{try{await i(e,{onSuccess:()=>{G.default.success("Model storage settings updated successfully"),c(),s?.()},onError:e=>{G.default.fromBackend("Failed to save model storage settings: "+(0,et.parseErrorMessage)(e))}})}catch(e){G.default.fromBackend("Failed to save model storage settings: "+(0,et.parseErrorMessage)(e))}},p=()=>{a.resetFields(),l()};return(0,t.jsx)(es.Modal,{title:(0,t.jsx)(R.Typography.Title,{level:5,children:"Model Settings"}),open:e,footer:(0,t.jsxs)(E.Space,{children:[(0,t.jsx)(Q.Button,{onClick:p,disabled:o||d,children:"Cancel"}),(0,t.jsx)(Q.Button,{type:"primary",loading:o,disabled:d,onClick:()=>a.submit(),children:o?"Saving...":"Save Settings"})]}),onCancel:p,children:(0,t.jsx)(el.Form,{form:a,layout:"horizontal",onFinish:h,initialValues:u,children:(0,t.jsx)(el.Form.Item,{label:"Store Model in DB",name:"store_model_in_db",tooltip:n?.find(e=>"store_model_in_db"===e.field_name)?.field_description||"If enabled, models and config are stored in and loaded from the database.",valuePropName:"checked",children:d?(0,t.jsx)(X.Skeleton.Input,{active:!0,block:!0}):(0,t.jsx)(L.Switch,{})})},n?JSON.stringify(u):"loading")})};var er=e.i(374009);let ei=(e,t)=>{if(!e?.data)return{data:[]};let l=JSON.parse(JSON.stringify(e.data));for(let e=0;e"model"!==e&&"api_base"!==e))),l[e].provider=o,l[e].input_cost=n,l[e].output_cost=d,l[e].litellm_model_name=a,null!=l[e].input_cost&&(l[e].input_cost=(1e6*Number(l[e].input_cost)).toFixed(2)),null!=l[e].output_cost&&(l[e].output_cost=(1e6*Number(l[e].output_cost)).toFixed(2)),l[e].max_tokens=c,l[e].max_input_tokens=m,l[e].api_base=s?.litellm_params?.api_base,l[e].cleanedLitellmParams=u}return{data:l}},{Text:eo}=R.Typography,en=({selectedModelGroup:e,setSelectedModelGroup:s,availableModelGroups:a,availableModelAccessGroups:i,setSelectedModelId:o,setSelectedTeamId:c})=>{let{data:m,isLoading:h}=(0,n.useModelCostMap)(),{accessToken:p,userId:g,userRole:f,premiumUser:_}=(0,r.default)(),{data:j,isLoading:y}=(0,u.useTeams)(),b=(0,$.useQueryClient)(),[v,P]=(0,x.useState)(""),[R,q]=(0,x.useState)(""),[Z,ee]=(0,x.useState)("current_team"),[et,el]=(0,x.useState)("personal"),[es,en]=(0,x.useState)(!1),[ed,ec]=(0,x.useState)(null),[em,eu]=(0,x.useState)(new Set),[eh,ep]=(0,x.useState)(1),[ex]=(0,x.useState)(50),[eg,ef]=(0,x.useState)({pageIndex:0,pageSize:50}),[e_,ej]=(0,x.useState)([]),[ey,eb]=(0,x.useState)(!1),ev=(0,x.useMemo)(()=>(0,er.default)(e=>{q(e),ep(1),ef(e=>({...e,pageIndex:0}))},200),[]);(0,x.useEffect)(()=>(ev(v),()=>{ev.cancel()}),[v,ev]);let eN="personal"===et?void 0:et.team_id,ew=(0,x.useMemo)(()=>{if(0===e_.length)return;let e=e_[0];return({input_cost:"costs",model_info_db_model:"status",model_info_created_by:"created_at",model_info_updated_at:"updated_at"})[e.id]||e.id},[e_]),eC=(0,x.useMemo)(()=>{if(0!==e_.length)return e_[0].desc?"desc":"asc"},[e_]),{data:ek,isLoading:eS,refetch:eT}=(0,d.useModelsInfo)(eh,ex,R||void 0,void 0,eN,ew,eC),eI=eS||h,eF=e=>null!=m&&"object"==typeof m&&e in m?m[e].litellm_provider:"openai",eP=(0,x.useMemo)(()=>ek?ei(ek,eF):{data:[]},[ek,m]),[eM,eA]=(0,x.useState)(null),[eE,eL]=(0,x.useState)(!1),eO=(0,x.useMemo)(()=>ek?{total_count:ek.total_count??0,current_page:ek.current_page??1,total_pages:ek.total_pages??1,size:ek.size??ex}:{total_count:0,current_page:1,total_pages:1,size:ex},[ek,ex]),eR=(0,x.useMemo)(()=>eP&&eP.data&&0!==eP.data.length?eP.data.filter(t=>{let l="all"===e||t.model_name===e||!e||"wildcard"===e&&t.model_name?.includes("*"),s="all"===ed||t.model_info.access_groups?.includes(ed)||!ed;return l&&s}):[],[eP,e,ed]);(0,x.useEffect)(()=>{ef(e=>({...e,pageIndex:0})),ep(1)},[e,ed]),(0,x.useEffect)(()=>{ep(1),ef(e=>({...e,pageIndex:0}))},[eN]),(0,x.useEffect)(()=>{ep(1),ef(e=>({...e,pageIndex:0}))},[e_]);let eB=(0,x.useMemo)(()=>eM&&eP?.data?eP.data.find(e=>e.model_info.id===eM):null,[eM,eP]),ez=async()=>{if(p&&eM)try{eL(!0),await (0,l.modelDeleteCall)(p,eM),G.default.success("Model deleted successfully"),b.invalidateQueries({queryKey:["models","list"]}),eT()}catch(e){console.error("Error deleting model:",e),G.default.fromBackend(e)}finally{eL(!1),eA(null)}},[eq,eV]=(0,x.useState)(null),eD=async(e,t)=>{if(p)try{eV(e),await (0,l.modelPatchUpdateCall)(p,{blocked:t},e),G.default.success(t?"Model paused":"Model resumed"),b.invalidateQueries({queryKey:["models","list"]})}catch(e){console.error("Error toggling model pause state:",e),G.default.fromBackend(e)}finally{eV(null)}};return(0,t.jsxs)(J.TabPanel,{children:[(0,t.jsx)(K.Grid,{children:(0,t.jsx)("div",{className:"flex flex-col space-y-4",children:(0,t.jsxs)("div",{className:"bg-white rounded-lg shadow-sm",children:[(0,t.jsxs)("div",{className:"border-b px-6 py-4 bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{className:"flex items-center gap-4",children:[(0,t.jsx)(eo,{className:"text-lg font-semibold text-gray-900",children:"Current Team:"}),(0,t.jsx)("div",{className:"w-80",children:eI?(0,t.jsx)(X.Skeleton.Input,{active:!0,block:!0,size:"large"}):(0,t.jsx)(Y.Select,{style:{width:"100%"},size:"large",defaultValue:"personal",value:"personal"===et?"personal":et.team_id,onChange:e=>{if("personal"===e)el("personal"),ep(1),ef(e=>({...e,pageIndex:0}));else{let t=j?.find(t=>t.team_id===e);t&&(el(t),ep(1),ef(e=>({...e,pageIndex:0})))}},loading:y,options:[{value:"personal",label:(0,t.jsxs)(E.Space,{direction:"horizontal",align:"center",children:[(0,t.jsx)(W.Badge,{color:"blue",size:"small"}),(0,t.jsx)(eo,{style:{fontSize:16},children:"Personal"})]})},...j?.filter(e=>e.team_id).map(e=>({value:e.team_id,label:(0,t.jsxs)(E.Space,{direction:"horizontal",align:"center",children:[(0,t.jsx)(W.Badge,{color:"green",size:"small"}),(0,t.jsx)(eo,{ellipsis:!0,style:{fontSize:16},children:e.team_alias?e.team_alias:e.team_id})]})}))??[]]})})]}),(0,t.jsxs)("div",{className:"flex items-center gap-4",children:[(0,t.jsx)(eo,{className:"text-lg font-semibold text-gray-900",children:"View:"}),(0,t.jsx)("div",{className:"w-64",children:eI?(0,t.jsx)(X.Skeleton.Input,{active:!0,block:!0,size:"large"}):(0,t.jsx)(Y.Select,{style:{width:"100%"},size:"large",defaultValue:"current_team",value:Z,onChange:e=>ee(e),options:[{value:"current_team",label:(0,t.jsxs)(E.Space,{direction:"horizontal",align:"center",children:[(0,t.jsx)(W.Badge,{color:"purple",size:"small"}),(0,t.jsx)(eo,{style:{fontSize:16},children:"Current Team Models"})]})},{value:"all",label:(0,t.jsxs)(E.Space,{direction:"horizontal",align:"center",children:[(0,t.jsx)(W.Badge,{color:"gray",size:"small"}),(0,t.jsx)(eo,{style:{fontSize:16},children:"All Available Models"})]})}]})})]})]}),"current_team"===Z&&(0,t.jsxs)("div",{className:"flex items-start gap-2 mt-3",children:[(0,t.jsx)(C.InfoCircleOutlined,{className:"text-gray-400 mt-0.5 shrink-0 text-xs"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"personal"===et?(0,t.jsxs)("span",{children:["To access these models: Create a Virtual Key without selecting a team on the"," ",(0,t.jsx)("a",{href:"/public?login=success&page=api-keys",className:"text-gray-600 hover:text-gray-800 underline",children:"Virtual Keys page"})]}):(0,t.jsxs)("span",{children:['To access these models: Create a Virtual Key and select Team as "',"string"!=typeof et?et.team_alias||et.team_id:"",'" on the'," ",(0,t.jsx)("a",{href:"/public?login=success&page=api-keys",className:"text-gray-600 hover:text-gray-800 underline",children:"Virtual Keys page"})]})})]})]}),(0,t.jsx)("div",{className:"border-b px-6 py-4",children:(0,t.jsxs)("div",{className:"flex flex-col space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,t.jsxs)("div",{className:"relative w-64",children:[(0,t.jsx)("input",{type:"text",placeholder:"Search model names...","data-testid":"model-search-input",className:"w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500",value:v,onChange:e=>P(e.target.value)}),(0,t.jsx)("svg",{className:"absolute left-2.5 top-2.5 h-4 w-4 text-gray-500",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"})})]}),(0,t.jsxs)("button",{className:`px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2 ${es?"bg-gray-100":""}`,onClick:()=>en(!es),children:[(0,t.jsx)("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"})}),"Filters"]}),(0,t.jsxs)("button",{className:"px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2",onClick:()=>{P(""),s("all"),ec(null),el("personal"),ee("current_team"),ep(1),ef({pageIndex:0,pageSize:50}),ej([])},children:[(0,t.jsx)("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"})}),"Reset Filters"]})]}),(0,t.jsx)(Q.Button,{icon:(0,t.jsx)(U.SettingOutlined,{}),onClick:()=>eb(!0),title:"Model Settings"})]}),es&&(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-3 mt-3",children:[(0,t.jsx)("div",{className:"w-64",children:(0,t.jsx)(Y.Select,{className:"w-full",value:e??"all",onChange:e=>s("all"===e?"all":e),placeholder:"Filter by Public Model Name",showSearch:!0,options:[{value:"all",label:"All Models"},{value:"wildcard",label:"Wildcard Models (*)"},...a.map((e,t)=>({value:e,label:e}))]})}),(0,t.jsx)("div",{className:"w-64",children:(0,t.jsx)(Y.Select,{className:"w-full",value:ed??"all",onChange:e=>ec("all"===e?null:e),placeholder:"Filter by Model Access Group",showSearch:!0,options:[{value:"all",label:"All Model Access Groups"},...i.map((e,t)=>({value:e,label:e}))]})})]}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[eI?(0,t.jsx)(X.Skeleton.Input,{active:!0,style:{width:184,height:20}}):(0,t.jsx)("span",{"data-testid":"models-results-count",className:"text-sm text-gray-700",children:eO.total_count>0?`Showing ${(eh-1)*ex+1} - ${Math.min(eh*ex,eO.total_count)} of ${eO.total_count} results`:"Showing 0 results"}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[eI?(0,t.jsx)(X.Skeleton.Button,{active:!0,style:{width:84,height:30}}):(0,t.jsx)("button",{onClick:()=>{ep(eh-1),ef(e=>({...e,pageIndex:0}))},disabled:1===eh,className:`px-3 py-1 text-sm border rounded-md ${1===eh?"bg-gray-100 text-gray-400 cursor-not-allowed":"hover:bg-gray-50"}`,children:"Previous"}),eI?(0,t.jsx)(X.Skeleton.Button,{active:!0,style:{width:56,height:30}}):(0,t.jsx)("button",{onClick:()=>{ep(eh+1),ef(e=>({...e,pageIndex:0}))},disabled:eh>=eO.total_pages,className:`px-3 py-1 text-sm border rounded-md ${eh>=eO.total_pages?"bg-gray-100 text-gray-400 cursor-not-allowed":"hover:bg-gray-50"}`,children:"Next"})]})]})]})}),(0,t.jsx)(N,{columns:[{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Model ID"}),accessorKey:"model_info.id",enableSorting:!1,size:130,minSize:80,cell:({row:e})=>{let l=e.original;return(0,t.jsx)(O.Tooltip,{title:l.model_info.id,children:(0,t.jsx)(z,{ellipsis:!0,className:"text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs cursor-pointer w-full block",style:{fontSize:14,padding:"1px 8px"},onClick:e=>{e.stopPropagation(),o(l.model_info.id)},children:l.model_info.id})})}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Model Information"}),accessorKey:"model_name",size:250,minSize:120,cell:({row:e})=>{let l=e.original,s=D(e.original)||"-",a=(0,t.jsxs)(E.Space,{direction:"vertical",size:12,style:{minWidth:220},children:[(0,t.jsxs)(M.Flex,{align:"center",gap:8,children:[(0,t.jsx)(B.ProviderLogo,{provider:l.provider}),(0,t.jsx)(z,{type:"secondary",style:{fontSize:12},ellipsis:!0,children:l.provider||"Unknown provider"})]}),(0,t.jsxs)(E.Space,{direction:"vertical",size:6,children:[(0,t.jsxs)(E.Space,{direction:"vertical",size:2,style:{width:"100%"},children:[(0,t.jsx)(z,{type:"secondary",style:{fontSize:11},children:"Public Model Name"}),(0,t.jsx)(z,{strong:!0,style:{fontSize:13,maxWidth:480},ellipsis:!0,title:s,children:s})]}),(0,t.jsxs)(E.Space,{direction:"vertical",size:2,children:[(0,t.jsx)(z,{type:"secondary",style:{fontSize:11},children:"LiteLLM Model Name"}),(0,t.jsx)(z,{style:{fontSize:13},copyable:{text:l.litellm_model_name||"-"},ellipsis:!0,title:l.litellm_model_name||"-",children:l.litellm_model_name||"-"})]})]})]});return(0,t.jsx)(A.Popover,{content:a,placement:"right",arrow:{pointAtCenter:!0},styles:{root:{maxWidth:500}},children:(0,t.jsxs)("div",{className:"flex items-start space-x-2 min-w-0 w-full cursor-pointer",children:[(0,t.jsx)("div",{className:"shrink-0 mt-0.5",children:l.provider?(0,t.jsx)(B.ProviderLogo,{provider:l.provider}):(0,t.jsx)("div",{className:"w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",children:"-"})}),(0,t.jsxs)("div",{className:"flex flex-col min-w-0 flex-1",children:[(0,t.jsx)(z,{ellipsis:!0,className:"text-gray-900",style:{fontSize:12,fontWeight:500,lineHeight:"16px"},children:s}),(0,t.jsx)(z,{ellipsis:!0,type:"secondary",style:{fontSize:12,lineHeight:"16px",marginTop:2},children:l.litellm_model_name||"-"})]})]})})}},{header:()=>(0,t.jsxs)("span",{className:"flex items-center gap-1",children:[(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Credentials"}),(0,t.jsx)(A.Popover,{content:V,placement:"bottom",arrow:{pointAtCenter:!0},children:(0,t.jsx)(C.InfoCircleOutlined,{className:"cursor-pointer text-gray-400 hover:text-gray-600",style:{fontSize:12}})})]}),accessorKey:"litellm_credential_name",enableSorting:!1,size:180,minSize:100,cell:({row:e})=>{let l=e.original,s=l.litellm_params?.litellm_credential_name,a=!!s;return(0,t.jsx)("div",{className:"flex items-center space-x-2 min-w-0 w-full",children:a?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(k.SyncOutlined,{className:"shrink-0",style:{color:"#1890ff",fontSize:14}}),(0,t.jsx)("span",{className:"text-xs truncate text-blue-600",title:s,children:s})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(w.EditOutlined,{className:"shrink-0",style:{color:"#8c8c8c",fontSize:14}}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Manual"})]})})}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Created By"}),accessorKey:"model_info.created_by",sortingFn:"datetime",size:160,minSize:100,cell:({row:e})=>{let l=e.original,s=!l.model_info?.db_model,a=l.model_info.created_by,r=l.model_info.created_at?new Date(l.model_info.created_at).toLocaleDateString():null;return(0,t.jsxs)("div",{className:"flex flex-col min-w-0 w-full",children:[(0,t.jsx)("div",{className:"text-xs font-medium text-gray-900 truncate",title:s?"Defined in config":a||"Unknown",children:s?"Defined in config":a||"Unknown"}),(0,t.jsx)("div",{className:"text-xs text-gray-500 truncate mt-0.5",title:s?"Config file":r||"Unknown date",children:s?"-":r||"Unknown date"})]})}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Updated At"}),accessorKey:"model_info.updated_at",sortingFn:"datetime",size:120,minSize:80,cell:({row:e})=>{let l=e.original;return(0,t.jsx)("span",{className:"text-xs",children:l.model_info.updated_at?new Date(l.model_info.updated_at).toLocaleDateString():"-"})}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Costs"}),accessorKey:"input_cost",size:120,minSize:80,cell:({row:e})=>{let l=e.original,s=l.input_cost,a=l.output_cost;return null==s&&null==a?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsx)("span",{className:"text-xs text-gray-400",children:"-"})}):(0,t.jsx)(O.Tooltip,{title:"Cost per 1M tokens",children:(0,t.jsxs)("div",{className:"flex flex-col min-w-0 w-full",children:[null!=s&&(0,t.jsxs)("div",{className:"text-xs font-medium text-gray-900 truncate",children:["In: $",s]}),null!=a&&(0,t.jsxs)("div",{className:"text-xs text-gray-500 truncate mt-0.5",children:["Out: $",a]})]})})}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Team ID"}),accessorKey:"model_info.team_id",enableSorting:!1,size:130,minSize:80,cell:({row:e})=>{let l=e.original;return l.model_info.team_id?(0,t.jsx)("div",{className:"overflow-hidden w-full",children:(0,t.jsx)(O.Tooltip,{title:l.model_info.team_id,children:(0,t.jsxs)(I.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate w-full",onClick:e=>{e.stopPropagation(),c(l.model_info.team_id)},children:[l.model_info.team_id.slice(0,7),"..."]})})}):"-"}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Model Access Group"}),accessorKey:"model_info.model_access_group",enableSorting:!1,size:180,minSize:100,cell:({row:e})=>{let l=e.original,s=l.model_info.access_groups;if(!s||0===s.length)return"-";let a=l.model_info.id,r=em.has(a),i=s.length>1;return(0,t.jsxs)("div",{className:"flex items-center gap-1 overflow-hidden w-full",children:[(0,t.jsx)(T.Badge,{size:"xs",color:"blue",className:"text-xs px-1.5 py-0.5 h-5 leading-tight shrink-0",children:s[0]}),(r||!i&&2===s.length)&&s.slice(1).map((e,l)=>(0,t.jsx)(T.Badge,{size:"xs",color:"blue",className:"text-xs px-1.5 py-0.5 h-5 leading-tight shrink-0",children:e},l+1)),i&&(0,t.jsx)("button",{onClick:e=>{let t;e.stopPropagation(),t=new Set(em),r?t.delete(a):t.add(a),eu(t)},className:"text-xs text-blue-600 hover:text-blue-800 px-1 py-0.5 rounded-sm hover:bg-blue-50 h-5 leading-tight shrink-0 whitespace-nowrap",children:r?"−":`+${s.length-1}`})]})}},{header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Status"}),accessorKey:"model_info.db_model",size:120,minSize:80,cell:({row:e})=>{let l=e.original;return(0,t.jsx)("div",{className:` - inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium - ${l.model_info.db_model?"bg-blue-50 text-blue-600":"bg-gray-100 text-gray-600"} - `,children:l.model_info.db_model?"DB Model":"Config Model"})}},{id:"actions",header:()=>(0,t.jsx)("span",{className:"text-sm font-semibold",children:"Actions"}),size:100,minSize:80,enableResizing:!1,cell:({row:e})=>{let l=e.original,s="Admin"===f||l.model_info?.created_by===g,a=!l.model_info?.db_model,r="Admin"===f,i=l.model_info?.blocked===!0,o=!a&&r&&!!eD,n=eq===l.model_info?.id;return(0,t.jsxs)("div",{className:"flex items-center justify-end gap-2 pr-4",children:[(0,t.jsx)(O.Tooltip,{title:a?"Config models cannot be paused from the dashboard. Pause is DB-backed.":r?i?"Resume model — restore normal routing.":"Pause model — stop routing requests until resumed.":"Only proxy admins can pause or resume a model.",children:(0,t.jsx)(L.Switch,{size:"small",checked:!i,disabled:!o||n,loading:n,"aria-label":i?"Resume model":"Pause model",onClick:(e,t)=>{t.stopPropagation()},onChange:e=>{let t=l.model_info?.id;o&&eD&&t&&eD(t,!e)}})}),a?(0,t.jsx)(O.Tooltip,{title:"Config model cannot be deleted on the dashboard. Please delete it from the config file.",children:(0,t.jsx)(F.Icon,{icon:S.TrashIcon,size:"sm",className:"opacity-50 cursor-not-allowed"})}):(0,t.jsx)(O.Tooltip,{title:"Delete model",children:(0,t.jsx)(F.Icon,{icon:S.TrashIcon,size:"sm",onClick:e=>{e.stopPropagation(),s&&eA&&eA(l.model_info.id)},className:s?"cursor-pointer hover:text-red-600":"opacity-50 cursor-not-allowed"})})]})}}],data:eR,isLoading:eS,sorting:e_,onSortingChange:ej,pagination:eg,onPaginationChange:ef,enablePagination:!0,onRowClick:e=>o(e.model_info.id)})]})})}),(0,t.jsx)(H.default,{isOpen:!!eM,title:"Delete Model",alertMessage:"This action cannot be undone.",message:"Are you sure you want to delete this model?",resourceInformationTitle:"Model Information",resourceInformation:eB?[{label:"Model Name",value:eB.model_name||"Not Set"},{label:"LiteLLM Model Name",value:eB.litellm_model_name||"Not Set"},{label:"Provider",value:eB.provider||"Not Set"},{label:"Created By",value:eB.model_info?.created_by||"Not Set"}]:[],onCancel:()=>eA(null),onOk:ez,confirmLoading:eE}),(0,t.jsx)(ea,{isVisible:ey,onCancel:()=>eb(!1),onSuccess:()=>eb(!1)})]})};var ed=e.i(206929),ec=e.i(35983),em=e.i(599724),eu=e.i(629569),eh=e.i(28651);let ep={"BadRequestError (400)":"BadRequestErrorRetries","AuthenticationError (401)":"AuthenticationErrorRetries","TimeoutError (408)":"TimeoutErrorRetries","RateLimitError (429)":"RateLimitErrorRetries","ContentPolicyViolationError (400)":"ContentPolicyViolationErrorRetries","InternalServerError (500)":"InternalServerErrorRetries"},ex=({selectedModelGroup:e,setSelectedModelGroup:l,availableModelGroups:s,globalRetryPolicy:a,setGlobalRetryPolicy:r,defaultRetry:i,modelGroupRetryPolicy:o,setModelGroupRetryPolicy:n,handleSaveRetrySettings:d,isSaving:c=!1})=>{let m="global"===e,u=(t,l)=>{n(s=>{let a={...s?.[e]??{}};return null==l?delete a[t]:a[t]=l,{...s??{},[e]:a}})};return(0,t.jsxs)(J.TabPanel,{children:[(0,t.jsx)("div",{className:"flex items-center gap-4 mb-6",children:(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(em.Text,{children:"Retry Policy Scope:"}),(0,t.jsxs)(ed.Select,{className:"ml-2 w-48",value:m?"global":e||s[0],onValueChange:e=>l(e),children:[(0,t.jsx)(ec.SelectItem,{value:"global",children:"Global Default"}),s.map((e,l)=>(0,t.jsx)(ec.SelectItem,{value:e,children:e},l))]})]})}),m?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eu.Title,{children:"Global Retry Policy"}),(0,t.jsx)(em.Text,{className:"mb-6",children:"Default retry settings applied to all model groups unless overridden"})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(eu.Title,{children:["Retry Policy for ",e]}),(0,t.jsx)(em.Text,{className:"mb-6",children:"Model-specific retry settings. Falls back to global defaults if not set."})]}),(0,t.jsx)("table",{children:(0,t.jsx)("tbody",{children:Object.entries(ep).map(([l,s],n)=>{let d=a?.[s]??i,c=m?void 0:o?.[e]?.[s],h=null!=c;return(0,t.jsxs)("tr",{className:"flex justify-between items-center mt-2",children:[(0,t.jsxs)("td",{children:[(0,t.jsx)(em.Text,{children:l}),!m&&(0,t.jsxs)(em.Text,{className:"text-xs text-gray-500 ml-2",children:["(Global: ",d,")"]})]}),(0,t.jsxs)("td",{className:"flex items-center gap-2",children:[(0,t.jsx)(eh.InputNumber,{className:"ml-5",value:m?d:h?c:null,placeholder:m?void 0:String(d),min:0,step:1,onChange:e=>m?void(null!=e&&r(t=>({...t??{},[s]:e}))):u(s,e)}),!m&&h&&(0,t.jsx)(I.Button,{variant:"light",size:"xs",onClick:()=>u(s,null),children:"Reset"})]})]},n)})})}),(0,t.jsx)(I.Button,{className:"mt-6 mr-8",onClick:d,loading:c,disabled:c,children:"Save"})]})};var eg=e.i(883552),ef=e.i(262218),e_=e.i(175712),ej=e.i(91979),ey=e.i(637235),eb=e.i(724154);e.i(247167);var ev=e.i(931067);let eN={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M811.4 418.7C765.6 297.9 648.9 212 512.2 212S258.8 297.8 213 418.6C127.3 441.1 64 519.1 64 612c0 110.5 89.5 200 199.9 200h496.2C870.5 812 960 722.5 960 612c0-92.7-63.1-170.7-148.6-193.3zm36.3 281a123.07 123.07 0 01-87.6 36.3H263.9c-33.1 0-64.2-12.9-87.6-36.3A123.3 123.3 0 01140 612c0-28 9.1-54.3 26.2-76.3a125.7 125.7 0 0166.1-43.7l37.9-9.9 13.9-36.6c8.6-22.8 20.6-44.1 35.7-63.4a245.6 245.6 0 0152.4-49.9c41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.2c19.9 14 37.5 30.8 52.4 49.9 15.1 19.3 27.1 40.7 35.7 63.4l13.8 36.5 37.8 10c54.3 14.5 92.1 63.8 92.1 120 0 33.1-12.9 64.3-36.3 87.7z"}}]},name:"cloud",theme:"outlined"};var ew=e.i(9583),eC=x.forwardRef(function(e,t){return x.createElement(ew.default,(0,ev.default)({},e,{ref:t,icon:eN}))}),ek=e.i(210612),eS=e.i(285027);let{Text:eT}=R.Typography,eI=({accessToken:e,onReloadSuccess:s,buttonText:a="Reload Price Data",showIcon:r=!0,size:i="middle",type:o="primary",className:n=""})=>{let[d,c]=(0,x.useState)(!1),[m,u]=(0,x.useState)(!1),[h,p]=(0,x.useState)(!1),[g,f]=(0,x.useState)(!1),[_,j]=(0,x.useState)(6),[y,b]=(0,x.useState)(null),[v,N]=(0,x.useState)(!1),[w,k]=(0,x.useState)(null),[S,T]=(0,x.useState)(!1);(0,x.useEffect)(()=>{I(),F();let e=setInterval(()=>{I(),F()},3e4);return()=>clearInterval(e)},[e]);let I=async()=>{if(e){N(!0);try{let t=await (0,l.getModelCostMapReloadStatus)(e);b(t)}catch(e){console.error("Failed to fetch reload status:",e),b({scheduled:!1,interval_hours:null,last_run:null,next_run:null})}finally{N(!1)}}},F=async()=>{if(e){T(!0);try{let t=await (0,l.getModelCostMapSource)(e);k(t)}catch(e){console.error("Failed to fetch cost map source info:",e)}finally{T(!1)}}},M=async()=>{if(!e)return void G.default.fromBackend("No access token available");c(!0);try{let t=await (0,l.reloadModelCostMap)(e);"success"===t.status?(G.default.success(`Price data reloaded successfully! ${t.models_count||0} models updated.`),s?.(),await I(),await F()):G.default.fromBackend("Failed to reload price data")}catch(e){console.error("Error reloading price data:",e),G.default.fromBackend("Failed to reload price data. Please try again.")}finally{c(!1)}},A=async()=>{if(!e)return void G.default.fromBackend("No access token available");if(_<=0)return void G.default.fromBackend("Hours must be greater than 0");u(!0);try{let t=await (0,l.scheduleModelCostMapReload)(e,_);"success"===t.status?(G.default.success(`Periodic reload scheduled for every ${_} hours`),f(!1),await I()):G.default.fromBackend("Failed to schedule periodic reload")}catch(e){console.error("Error scheduling reload:",e),G.default.fromBackend("Failed to schedule periodic reload. Please try again.")}finally{u(!1)}},L=async()=>{if(!e)return void G.default.fromBackend("No access token available");p(!0);try{let t=await (0,l.cancelModelCostMapReload)(e);"success"===t.status?(G.default.success("Periodic reload cancelled successfully"),await I()):G.default.fromBackend("Failed to cancel periodic reload")}catch(e){console.error("Error cancelling reload:",e),G.default.fromBackend("Failed to cancel periodic reload. Please try again.")}finally{p(!1)}},R=e=>{if(!e)return"Never";try{return new Date(e).toLocaleString()}catch{return e}};return(0,t.jsxs)("div",{className:n,children:[(0,t.jsxs)(E.Space,{direction:"horizontal",size:"middle",style:{marginBottom:16},children:[(0,t.jsx)(eg.Popconfirm,{title:"Hard Refresh Price Data",description:"This will immediately fetch the latest pricing information from the remote source. Continue?",onConfirm:M,okText:"Yes",cancelText:"No",okButtonProps:{style:{backgroundColor:"#6366f1",borderColor:"#6366f1",color:"white",fontWeight:"500",borderRadius:"0.375rem",padding:"0.375rem 0.75rem",height:"auto",fontSize:"0.875rem",lineHeight:"1.25rem",transition:"all 0.2s ease-in-out"},onMouseEnter:e=>{e.currentTarget.style.backgroundColor="#4f46e5"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="#6366f1"}},children:(0,t.jsx)(Q.Button,{type:o,size:i,loading:d,icon:r?(0,t.jsx)(ej.ReloadOutlined,{}):void 0,style:{backgroundColor:"#6366f1",borderColor:"#6366f1",color:"white",fontWeight:"500",borderRadius:"0.375rem",padding:"0.375rem 0.75rem",height:"auto",fontSize:"0.875rem",lineHeight:"1.25rem",transition:"all 0.2s ease-in-out"},onMouseEnter:e=>{e.currentTarget.style.backgroundColor="#4f46e5"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="#6366f1"},children:a})}),y?.scheduled?(0,t.jsx)(Q.Button,{type:"default",size:i,danger:!0,icon:(0,t.jsx)(eb.StopOutlined,{}),loading:h,onClick:L,style:{borderColor:"#ff4d4f",color:"#ff4d4f",fontWeight:"500",borderRadius:"0.375rem",padding:"0.375rem 0.75rem",height:"auto",fontSize:"0.875rem",lineHeight:"1.25rem"},children:"Cancel Periodic Reload"}):(0,t.jsx)(Q.Button,{type:"default",size:i,icon:(0,t.jsx)(ey.ClockCircleOutlined,{}),onClick:()=>f(!0),style:{borderColor:"#d9d9d9",color:"#6366f1",fontWeight:"500",borderRadius:"0.375rem",padding:"0.375rem 0.75rem",height:"auto",fontSize:"0.875rem",lineHeight:"1.25rem"},children:"Set Up Periodic Reload"})]}),w&&(0,t.jsx)(e_.Card,{size:"small",style:{backgroundColor:"remote"===w.source?"#f0f7ff":"#fff8f0",border:`1px solid ${"remote"===w.source?"#bae0ff":"#ffd591"}`,borderRadius:8,marginBottom:12},children:(0,t.jsxs)(E.Space,{direction:"vertical",size:"small",style:{width:"100%"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:["remote"===w.source?(0,t.jsx)(eC,{style:{color:"#1677ff",fontSize:16}}):(0,t.jsx)(ek.DatabaseOutlined,{style:{color:"#fa8c16",fontSize:16}}),(0,t.jsx)(eT,{strong:!0,style:{fontSize:"13px"},children:"Pricing Data Source"}),(0,t.jsx)(ef.Tag,{color:"remote"===w.source?"blue":"orange",style:{marginLeft:"auto",fontWeight:600,textTransform:"uppercase",fontSize:"11px"},children:"remote"===w.source?"Remote":"Local"})]}),(0,t.jsx)(P.Divider,{style:{margin:"6px 0"}}),(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,t.jsx)(eT,{type:"secondary",style:{fontSize:"12px"},children:"Models loaded:"}),(0,t.jsx)(eT,{strong:!0,style:{fontSize:"12px"},children:w.model_count.toLocaleString()})]}),w.url&&(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"flex-start",gap:8},children:[(0,t.jsx)(eT,{type:"secondary",style:{fontSize:"12px",whiteSpace:"nowrap"},children:"remote"===w.source?"Loaded from:":"Attempted URL:"}),(0,t.jsx)(O.Tooltip,{title:w.url,children:(0,t.jsx)(eT,{style:{fontSize:"11px",maxWidth:240,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",display:"block",color:"#1677ff",cursor:"default"},children:w.url})})]}),w.is_env_forced&&(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:6,marginTop:2},children:[(0,t.jsx)(C.InfoCircleOutlined,{style:{color:"#fa8c16",fontSize:12}}),(0,t.jsxs)(eT,{type:"secondary",style:{fontSize:"11px"},children:["Local mode forced via ",(0,t.jsx)("code",{children:"LITELLM_LOCAL_MODEL_COST_MAP=True"})]})]}),w.fallback_reason&&(0,t.jsxs)("div",{style:{display:"flex",alignItems:"flex-start",gap:6,backgroundColor:"#fff7e6",border:"1px solid #ffd591",borderRadius:4,padding:"4px 8px",marginTop:2},children:[(0,t.jsx)(eS.WarningOutlined,{style:{color:"#fa8c16",fontSize:12,marginTop:2}}),(0,t.jsxs)(eT,{style:{fontSize:"11px",color:"#614700"},children:["Fell back to local: ",w.fallback_reason]})]})]})}),y&&(0,t.jsx)(e_.Card,{size:"small",style:{backgroundColor:"#f8f9fa",border:"1px solid #e9ecef",borderRadius:8},children:(0,t.jsxs)(E.Space,{direction:"vertical",size:"small",style:{width:"100%"},children:[y.scheduled?(0,t.jsx)("div",{children:(0,t.jsxs)(ef.Tag,{color:"green",icon:(0,t.jsx)(ey.ClockCircleOutlined,{}),children:["Scheduled every ",y.interval_hours," hours"]})}):(0,t.jsx)(eT,{type:"secondary",children:"No periodic reload scheduled"}),(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,t.jsx)(eT,{type:"secondary",style:{fontSize:"12px"},children:"Last run:"}),(0,t.jsx)(eT,{style:{fontSize:"12px"},children:R(y.last_run)})]}),y.scheduled&&(0,t.jsxs)(t.Fragment,{children:[y.next_run&&(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,t.jsx)(eT,{type:"secondary",style:{fontSize:"12px"},children:"Next run:"}),(0,t.jsx)(eT,{style:{fontSize:"12px"},children:R(y.next_run)})]}),(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,t.jsx)(eT,{type:"secondary",style:{fontSize:"12px"},children:"Status:"}),(0,t.jsx)(ef.Tag,{color:y?.scheduled?y.last_run?"success":"processing":"default",children:y?.scheduled?y.last_run?"Active":"Ready":"Not scheduled"})]})]})]})}),(0,t.jsxs)(es.Modal,{title:"Set Up Periodic Reload",open:g,onOk:A,onCancel:()=>f(!1),confirmLoading:m,okText:"Schedule",cancelText:"Cancel",okButtonProps:{style:{backgroundColor:"#6366f1",borderColor:"#6366f1",color:"white"}},children:[(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(eT,{children:"Set up automatic reload of price data every:"})}),(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(eh.InputNumber,{min:1,max:168,value:_,onChange:e=>j(e||6),addonAfter:"hours",style:{width:"100%"}})}),(0,t.jsx)("div",{children:(0,t.jsxs)(eT,{type:"secondary",children:["This will automatically fetch the latest pricing data from the remote source every ",_," hours."]})})]})]})},eF=()=>{let{accessToken:e}=(0,r.default)(),{refetch:l}=(0,n.useModelCostMap)();return(0,t.jsx)(J.TabPanel,{children:(0,t.jsxs)("div",{className:"p-6",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(eu.Title,{children:"Price Data Management"}),(0,t.jsx)(em.Text,{className:"text-tremor-content",children:"Manage model pricing data and configure automatic reload schedules"})]}),(0,t.jsx)(eI,{accessToken:e,onReloadSuccess:()=>{l()},buttonText:"Reload Price Data",size:"middle",type:"primary",className:"w-full"})]})})};var eP=e.i(916925);let eM=async(e,t,l)=>{try{let t=e.model_mappings||[];if("model_mappings"in e&&delete e.model_mappings,e.model&&e.model.includes("all-wildcard")){let l=e.custom_llm_provider,s=(eP.provider_map[l]??l.toLowerCase())+"/*";e.model_name=s,t.push({public_name:s,litellm_model:s}),e.model=s}let l=[];for(let s of t){let t={},a={},r=s.public_name;for(let[l,r]of(t.model=s.litellm_model,void 0!==e.input_cost_per_token&&null!==e.input_cost_per_token&&""!==e.input_cost_per_token&&(e.input_cost_per_token=Number(e.input_cost_per_token)/1e6),void 0!==e.output_cost_per_token&&null!==e.output_cost_per_token&&""!==e.output_cost_per_token&&(e.output_cost_per_token=Number(e.output_cost_per_token)/1e6),void 0!==e.cache_read_input_token_cost&&null!==e.cache_read_input_token_cost&&""!==e.cache_read_input_token_cost?e.cache_read_input_token_cost=Number(e.cache_read_input_token_cost)/1e6:void 0!==e.input_cost_per_token&&null!==e.input_cost_per_token&&""!==e.input_cost_per_token?e.cache_read_input_token_cost=Number(e.input_cost_per_token):delete e.cache_read_input_token_cost,void 0!==e.cache_creation_input_token_cost&&null!==e.cache_creation_input_token_cost&&""!==e.cache_creation_input_token_cost?e.cache_creation_input_token_cost=Number(e.cache_creation_input_token_cost)/1e6:delete e.cache_creation_input_token_cost,t.model=s.litellm_model,Object.entries(e)))if(""!==r&&"custom_pricing"!==l&&"pricing_model"!==l&&"cache_control"!==l)if("model_name"==l)t.model=r;else if("custom_llm_provider"==l)t.custom_llm_provider=eP.provider_map[r]??r.toLowerCase();else if("model"==l)continue;else if("base_model"===l)a[l]=r;else if("team_id"===l)a.team_id=r;else if("model_access_group"===l)a.access_groups=r;else if("mode"==l)a.mode=r,delete t.mode;else if("custom_model_name"===l)t.model=r;else if("litellm_extra_params"==l){let e={};if(r&&void 0!=r){try{e=JSON.parse(r),"litellm_credential_name"in e&&delete e.litellm_credential_name}catch(e){throw G.default.fromBackend("Failed to parse LiteLLM Extra Params: "+e),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,s]of Object.entries(e))t[l]=s}}else if("model_info_params"==l){let e={};if(r&&void 0!=r){try{e=JSON.parse(r)}catch(e){throw G.default.fromBackend("Failed to parse LiteLLM Extra Params: "+e),Error("Failed to parse litellm_extra_params: "+e)}for(let[t,l]of Object.entries(e))a[t]=l}}else if("input_cost_per_token"===l||"output_cost_per_token"===l||"input_cost_per_second"===l||"cache_read_input_token_cost"===l||"cache_creation_input_token_cost"===l){null!=r&&""!==r&&(t[l]=Number(r));continue}else t[l]=r;l.push({litellmParamsObj:t,modelInfoObj:a,modelName:r})}return l}catch(e){G.default.fromBackend("Failed to create model: "+e)}},eA=async(e,t,s,a)=>{try{let r=await eM(e,t,s);if(!r||0===r.length)return;for(let e of r){let{litellmParamsObj:s,modelInfoObj:a,modelName:r}=e,i={model_name:r,litellm_params:s,model_info:a};await (0,l.modelCreateCall)(t,i)}a&&a(),s.resetFields()}catch(e){G.default.fromBackend("Failed to add model: "+e)}};var eE=e.i(591935),eL=e.i(304967),eO=e.i(779241);let eR=(0,a.createQueryKeys)("providerFields"),eB=()=>(0,s.useQuery)({queryKey:eR.list({}),queryFn:async()=>await (0,l.getProviderCreateMetadata)(),staleTime:864e5,gcTime:864e5});var ez=e.i(519756),eq=e.i(178654),eV=e.i(311451),eD=e.i(621192),eH=e.i(515831);let{Link:eG}=R.Typography,eU=e=>{let t="password"===e.field_type?"password":"select"===e.field_type?"select":"upload"===e.field_type?"upload":"textarea"===e.field_type?"textarea":"text";return{key:e.key,label:e.label,placeholder:e.placeholder??void 0,tooltip:e.tooltip??void 0,required:e.required??!1,type:t,options:e.options??void 0,defaultValue:e.default_value??void 0}},e$={},eK=({selectedProvider:e,uploadProps:l})=>{let s=eP.Providers[e],a=el.Form.useFormInstance(),{data:r,isLoading:i,error:o}=eB(),n=x.default.useMemo(()=>{if(!r)return null;let e={};return r.forEach(t=>{let l=t.provider_display_name,s=t.credential_fields.map(eU);e[l]=s,t.provider&&(e[t.provider]=s),t.litellm_provider&&(e[t.litellm_provider]=s)}),e},[r]);x.default.useEffect(()=>{n&&Object.assign(e$,n)},[n]);let d=x.default.useMemo(()=>{let t=e$[s]??e$[e];if(t)return t;if(!r)return[];let l=r.find(t=>t.provider_display_name===s||t.provider===e||t.litellm_provider===e);if(!l)return[];let a=l.credential_fields.map(eU);return e$[l.provider_display_name]=a,l.provider&&(e$[l.provider]=a),l.litellm_provider&&(e$[l.litellm_provider]=a),a},[s,e,r]),c=x.default.useMemo(()=>d.some(e=>"api_version"===e.key),[d]),m=x.default.useRef(null),u=x.default.useCallback(e=>{if(!c)return;let t=(e=>{let t=e.indexOf("?");if(-1===t)return null;let l=new URLSearchParams(e.slice(t+1).split("#")[0]);return l.get("api_version")||l.get("api-version")})(e.target.value);if(t){m.current=t,a.setFieldsValue({api_version:t});return}a.getFieldValue("api_version")===m.current&&a.setFieldsValue({api_version:""}),m.current=null},[a,c]),h={name:"file",accept:".json",beforeUpload:e=>{if("application/json"===e.type){let t=new FileReader;t.onload=e=>{if(e.target){let t=e.target.result;a.setFieldsValue({vertex_credentials:t})}},t.readAsText(e)}return!1}};return(0,t.jsxs)(t.Fragment,{children:[i&&0===d.length&&(0,t.jsx)(eD.Row,{children:(0,t.jsx)(eq.Col,{span:24,children:(0,t.jsx)(em.Text,{className:"mb-2",children:"Loading provider fields..."})})}),o&&0===d.length&&(0,t.jsx)(eD.Row,{children:(0,t.jsx)(eq.Col,{span:24,children:(0,t.jsx)(em.Text,{className:"mb-2 text-red-500",children:o instanceof Error?o.message:"Failed to load provider credential fields"})})}),d.map(e=>(0,t.jsxs)(x.default.Fragment,{children:[(0,t.jsx)(el.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:"Required"}]:void 0,tooltip:e.tooltip,className:"vertex_credentials"===e.key?"mb-0":void 0,children:"select"===e.type?(0,t.jsx)(Y.Select,{placeholder:e.placeholder,defaultValue:e.defaultValue,children:e.options?.map(e=>(0,t.jsx)(Y.Select.Option,{value:e,children:e},e))}):"upload"===e.type?(0,t.jsx)(eH.Upload,{...h,onChange:e=>{l?.onChange&&l.onChange(e)},children:(0,t.jsx)(Q.Button,{icon:(0,t.jsx)(ez.UploadOutlined,{}),children:"Click to Upload"})}):"textarea"===e.type?(0,t.jsx)(eV.Input.TextArea,{placeholder:e.placeholder,defaultValue:e.defaultValue,rows:6,style:{fontFamily:"monospace",fontSize:"12px"}}):(0,t.jsx)(eO.TextInput,{placeholder:e.placeholder,type:"password"===e.type?"password":"text",defaultValue:e.defaultValue,onChange:"api_base"===e.key?u:void 0})}),"vertex_credentials"===e.key&&(0,t.jsx)(eD.Row,{children:(0,t.jsx)(eq.Col,{children:(0,t.jsx)(em.Text,{className:"mb-3 mt-1",children:"Give a gcp service account(.json file)"})})}),"base_model"===e.key&&(0,t.jsxs)(eD.Row,{children:[(0,t.jsx)(eq.Col,{span:10}),(0,t.jsx)(eq.Col,{span:10,children:(0,t.jsxs)(em.Text,{className:"mb-2",children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from"," ",(0,t.jsx)(eG,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})})]})]},e.key))]})};var eJ=e.i(555987);function eW(e,t,l){let s=e.getFieldValue("credential_name");e.resetFields(),void 0!==s&&e.setFieldValue("credential_name",s),l(t),e.setFieldValue("custom_llm_provider",t)}let{Link:eQ}=R.Typography,eY=({open:e,onCancel:l,onAddCredential:s,uploadProps:a})=>{let[r]=el.Form.useForm(),[i,o]=(0,x.useState)(eP.Providers.OpenAI);return(0,t.jsx)(es.Modal,{title:"Add New Credential",open:e,onCancel:()=>{l(),r.resetFields()},footer:null,width:600,children:(0,t.jsxs)(el.Form,{form:r,onFinish:e=>{s(Object.entries(e).reduce((e,[t,l])=>(""!==l&&null!=l&&(e[t]=l),e),{})),r.resetFields()},layout:"vertical",children:[(0,t.jsx)(el.Form.Item,{label:"Credential Name:",name:"credential_name",rules:[{required:!0,message:"Credential name is required"}],children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter a friendly name for these credentials"})}),(0,t.jsx)(el.Form.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"Helper to auto-populate provider specific fields",children:(0,t.jsx)(Y.Select,{showSearch:!0,onChange:e=>{eW(r,e,o)},children:Object.entries(eP.Providers).map(([e,l])=>(0,t.jsx)(Y.Select.Option,{value:e,children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("img",{src:(0,eJ.resolveLogoSrc)(eP.providerLogoMap[l]),alt:`${e} logo`,className:"w-5 h-5",onError:e=>{let t=e.target,s=t.parentElement;if(s){let e=document.createElement("div");e.className="w-5 h-5 rounded-full bg-gray-200 flex items-center justify-center text-xs",e.textContent=l.charAt(0),s.replaceChild(e,t)}}}),(0,t.jsx)("span",{children:l})]})},e))})}),(0,t.jsx)(eK,{selectedProvider:i,uploadProps:a}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)(O.Tooltip,{title:"Get help on our github",children:(0,t.jsx)(eQ,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})}),(0,t.jsxs)("div",{children:[(0,t.jsx)(Q.Button,{onClick:()=>{l(),r.resetFields()},style:{marginRight:10},children:"Cancel"}),(0,t.jsx)(Q.Button,{htmlType:"submit",children:"Add Credential"})]})]})]})})},{Link:eX}=R.Typography;function eZ({open:e,onCancel:l,onUpdateCredential:s,uploadProps:a,existingCredential:r}){let[i]=el.Form.useForm(),[o,n]=(0,x.useState)(eP.Providers.Anthropic);return(0,x.useEffect)(()=>{if(r){let e=Object.entries(r.credential_values||{}).reduce((e,[t,l])=>(e[t]=l??null,e),{});i.setFieldsValue({credential_name:r.credential_name,custom_llm_provider:r.credential_info.custom_llm_provider,...e}),n(r.credential_info.custom_llm_provider)}},[r]),(0,t.jsx)(es.Modal,{title:"Edit Credential",open:e,onCancel:()=>{l(),i.resetFields()},footer:null,width:600,destroyOnHidden:!0,children:(0,t.jsxs)(el.Form,{form:i,onFinish:e=>{s(Object.entries(e).reduce((e,[t,l])=>(""!==l&&null!=l&&(e[t]=l),e),{})),i.resetFields()},layout:"vertical",children:[(0,t.jsx)(el.Form.Item,{label:"Credential Name:",name:"credential_name",rules:[{required:!0,message:"Credential name is required"}],initialValue:r?.credential_name,children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter a friendly name for these credentials",disabled:!!r?.credential_name})}),(0,t.jsx)(el.Form.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"Helper to auto-populate provider specific fields",children:(0,t.jsx)(Y.Select,{showSearch:!0,onChange:e=>{eW(i,e,n)},children:Object.entries(eP.Providers).map(([e,l])=>(0,t.jsx)(Y.Select.Option,{value:e,children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("img",{src:(0,eJ.resolveLogoSrc)(eP.providerLogoMap[l]),alt:`${e} logo`,className:"w-5 h-5",onError:e=>{let t=e.target,s=t.parentElement;if(s){let e=document.createElement("div");e.className="w-5 h-5 rounded-full bg-gray-200 flex items-center justify-center text-xs",e.textContent=l.charAt(0),s.replaceChild(e,t)}}}),(0,t.jsx)("span",{children:l})]})},e))})}),(0,t.jsx)(eK,{selectedProvider:o,uploadProps:a}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)(O.Tooltip,{title:"Get help on our github",children:(0,t.jsx)(eX,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})}),(0,t.jsxs)("div",{children:[(0,t.jsx)(Q.Button,{onClick:()=>{l(),i.resetFields()},style:{marginRight:10},children:"Cancel"}),(0,t.jsx)(Q.Button,{htmlType:"submit",children:"Update Credential"})]})]})]})})}var e0=e.i(708347);let e1=({uploadProps:e})=>{let{accessToken:s,userRole:a}=(0,r.default)(),i=(0,e0.isProxyAdminRole)(a??""),{data:n,refetch:d}=o(),c=n?.credentials||[],[m,u]=(0,x.useState)(!1),[h,p]=(0,x.useState)(!1),[v,N]=(0,x.useState)(null),[w,C]=(0,x.useState)(null),[k,F]=(0,x.useState)(!1),[P,M]=(0,x.useState)(!1),[A]=el.Form.useForm(),E=["credential_name","custom_llm_provider"],L=async e=>{if(!s)return;let t=Object.entries(e).filter(([e])=>!E.includes(e)).reduce((e,[t,l])=>({...e,[t]:l}),{}),a={credential_name:e.credential_name,credential_values:t,credential_info:{custom_llm_provider:e.custom_llm_provider}};await (0,l.credentialUpdateCall)(s,e.credential_name,a),G.default.success("Credential updated successfully"),p(!1),await d()},O=async e=>{if(!s)return;let t=Object.entries(e).filter(([e])=>!E.includes(e)).reduce((e,[t,l])=>({...e,[t]:l}),{}),a={credential_name:e.credential_name,credential_values:t,credential_info:{custom_llm_provider:e.custom_llm_provider}};await (0,l.credentialCreateCall)(s,a),G.default.success("Credential added successfully"),u(!1),await d()},R=async()=>{if(s&&w){M(!0);try{await (0,l.credentialDeleteCall)(s,w.credential_name),G.default.success("Credential deleted successfully"),await d()}catch(e){G.default.error("Failed to delete credential")}finally{C(null),F(!1),M(!1)}}};return(0,t.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto p-2",children:[i&&(0,t.jsx)(I.Button,{onClick:()=>u(!0),children:"Add Credential"}),(0,t.jsx)("div",{className:"flex justify-between items-center mt-4 mb-4",children:(0,t.jsx)(em.Text,{children:"Configured credentials for different AI providers. Add and manage your API credentials."})}),(0,t.jsx)(eL.Card,{children:(0,t.jsxs)(g.Table,{children:[(0,t.jsx)(f.TableHead,{children:(0,t.jsxs)(y.TableRow,{children:[(0,t.jsx)(_.TableHeaderCell,{children:"Credential Name"}),(0,t.jsx)(_.TableHeaderCell,{children:"Provider"}),(0,t.jsx)(_.TableHeaderCell,{children:"Actions"})]})}),(0,t.jsx)(j.TableBody,{children:c&&0!==c.length?c.map((e,l)=>{var s;let a,r;return(0,t.jsxs)(y.TableRow,{children:[(0,t.jsx)(b.TableCell,{children:e.credential_name}),(0,t.jsx)(b.TableCell,{children:(s=e.credential_info?.custom_llm_provider||"-",r=(a={openai:"blue",azure:"indigo",anthropic:"purple",default:"gray"})[s.toLowerCase()]||a.default,(0,t.jsx)(T.Badge,{color:r,size:"xs",children:s}))}),(0,t.jsx)(b.TableCell,{children:i?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(I.Button,{icon:eE.PencilAltIcon,variant:"light",size:"sm",onClick:()=>{N(e),p(!0)}}),(0,t.jsx)(I.Button,{icon:S.TrashIcon,variant:"light",size:"sm",onClick:()=>{C(e),F(!0)},className:"ml-2"})]}):null})]},l)}):(0,t.jsx)(y.TableRow,{children:(0,t.jsx)(b.TableCell,{colSpan:4,className:"text-center py-4 text-gray-500",children:"No credentials configured"})})})]})}),m&&(0,t.jsx)(eY,{onAddCredential:O,open:m,onCancel:()=>u(!1),uploadProps:e}),h&&(0,t.jsx)(eZ,{open:h,existingCredential:v,onUpdateCredential:L,uploadProps:e,onCancel:()=>p(!1)}),(0,t.jsx)(H.default,{isOpen:k,onCancel:()=>{C(null),F(!1)},onOk:R,title:"Delete Credential?",message:"Are you sure you want to delete this credential? This action cannot be undone and may break existing integrations.",resourceInformationTitle:"Credential Information",resourceInformation:[{label:"Credential Name",value:w?.credential_name},{label:"Provider",value:w?.credential_info?.custom_llm_provider||"-"}],confirmLoading:P,requiredConfirmation:w?.credential_name})]})};var e2=e.i(278587),e4=e.i(309426),e5=e.i(197647),e6=e.i(653824),e3=e.i(881073),e8=e.i(723731),e7=e.i(475647),e9=e.i(91739),te=e.i(437902),tt=e.i(166406);let{Text:tl}=R.Typography,ts=({formValues:e,accessToken:s,testMode:a,modelName:r="this model",onClose:i,onTestComplete:o})=>{var n,d,c;let m,u,[h,p]=x.default.useState(null),[g,f]=x.default.useState(null),[_,j]=x.default.useState(null),[y,b]=x.default.useState(!0),[v,N]=x.default.useState(!1),[w,k]=x.default.useState(!1),S=async()=>{b(!0),k(!1),p(null),f(null),j(null),N(!1),await new Promise(e=>setTimeout(e,100));try{let t=await eM(e,s,null);if(!t){p("Failed to prepare model data. Please check your form inputs."),N(!1),b(!1);return}let{litellmParamsObj:a,modelInfoObj:r,modelName:i}=t[0],o=await (0,l.testConnectionRequest)(s,a,r,r?.mode);if("success"===o.status)G.default.success("Connection test successful!"),p(null),N(!0);else{let e=o.result?.error||o.message||"Unknown error";p(e),f(a),j(o.result?.raw_request_typed_dict),N(!1)}}catch(e){console.error("Test connection error:",e),p(e instanceof Error?e.message:String(e)),N(!1)}finally{b(!1),o&&o()}};x.default.useEffect(()=>{let e=setTimeout(()=>{S()},200);return()=>clearTimeout(e)},[]);let T=e=>e?e.split("stack trace:")[0].trim().replace(/^litellm\.(.*?)Error: /,""):"Unknown error",I="string"==typeof h?T(h):h?.message?T(h.message):"Unknown error",F=_?(n=_.raw_request_api_base,d=_.raw_request_body,c=_.raw_request_headers||{},m=JSON.stringify(d,null,2).split("\n").map(e=>` ${e}`).join("\n"),u=Object.entries(c).map(([e,t])=>`-H '${e}: ${t}'`).join(" \\\n "),`curl -X POST \\ - ${n} \\ - ${u?`${u} \\ - `:""}-H 'Content-Type: application/json' \\ - -d '{ -${m} - }'`):"";return(0,t.jsxs)("div",{style:{padding:"24px",borderRadius:"8px",backgroundColor:"#fff"},children:[y?(0,t.jsxs)("div",{style:{textAlign:"center",padding:"32px 20px"},className:"jsx-dc9a0e2d897fe63b",children:[(0,t.jsx)("div",{style:{marginBottom:"16px"},className:"jsx-dc9a0e2d897fe63b loading-spinner",children:(0,t.jsx)("div",{style:{border:"3px solid #f3f3f3",borderTop:"3px solid #1890ff",borderRadius:"50%",width:"30px",height:"30px",animation:"spin 1s linear infinite",margin:"0 auto"},className:"jsx-dc9a0e2d897fe63b"})}),(0,t.jsxs)(tl,{style:{fontSize:"16px"},children:["Testing connection to ",r,"..."]}),(0,t.jsx)(te.default,{id:"dc9a0e2d897fe63b",children:"@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}"})]}):v?(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",padding:"32px 20px"},children:[(0,t.jsx)("div",{style:{color:"#52c41a",fontSize:"24px",display:"flex",alignItems:"center"},children:(0,t.jsx)("svg",{viewBox:"64 64 896 896",focusable:"false","data-icon":"check-circle",width:"1em",height:"1em",fill:"currentColor","aria-hidden":"true",children:(0,t.jsx)("path",{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"})})}),(0,t.jsxs)(tl,{"data-testid":"connection-success-msg",type:"success",style:{fontSize:"18px",fontWeight:500,marginLeft:"10px"},children:["Connection to ",r," successful!"]})]}):(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",marginBottom:"20px"},children:[(0,t.jsx)(eS.WarningOutlined,{style:{color:"#ff4d4f",fontSize:"24px",marginRight:"12px"}}),(0,t.jsxs)(tl,{"data-testid":"connection-failure-msg",type:"danger",style:{fontSize:"18px",fontWeight:500},children:["Connection to ",r," failed"]})]}),(0,t.jsxs)("div",{style:{backgroundColor:"#fff2f0",border:"1px solid #ffccc7",borderRadius:"8px",padding:"16px",marginBottom:"20px",boxShadow:"0 1px 2px rgba(0, 0, 0, 0.03)"},children:[(0,t.jsxs)(tl,{strong:!0,style:{display:"block",marginBottom:"8px"},children:["Error:"," "]}),(0,t.jsx)(tl,{type:"danger",style:{fontSize:"14px",lineHeight:"1.5"},children:I}),h&&(0,t.jsx)("div",{style:{marginTop:"12px"},children:(0,t.jsx)(Q.Button,{type:"link",onClick:()=>k(!w),style:{paddingLeft:0,height:"auto"},children:w?"Hide Details":"Show Details"})})]}),w&&(0,t.jsxs)("div",{style:{marginBottom:"20px"},children:[(0,t.jsx)(tl,{strong:!0,style:{display:"block",marginBottom:"8px",fontSize:"15px"},children:"Troubleshooting Details"}),(0,t.jsx)("pre",{style:{backgroundColor:"#f5f5f5",padding:"16px",borderRadius:"8px",fontSize:"13px",maxHeight:"200px",overflow:"auto",border:"1px solid #e8e8e8",lineHeight:"1.5"},children:"string"==typeof h?h:JSON.stringify(h,null,2)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(tl,{strong:!0,style:{display:"block",marginBottom:"8px",fontSize:"15px"},children:"API Request"}),(0,t.jsx)("pre",{style:{backgroundColor:"#f5f5f5",padding:"16px",borderRadius:"8px",fontSize:"13px",maxHeight:"250px",overflow:"auto",border:"1px solid #e8e8e8",lineHeight:"1.5"},children:F||"No request data available"}),(0,t.jsx)(Q.Button,{style:{marginTop:"8px"},icon:(0,t.jsx)(tt.CopyOutlined,{}),onClick:()=>{navigator.clipboard.writeText(F||""),G.default.success("Copied to clipboard")},children:"Copy to Clipboard"})]})]})}),(0,t.jsx)(P.Divider,{style:{margin:"24px 0 16px"}}),(0,t.jsx)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:(0,t.jsx)(Q.Button,{type:"link",href:"https://docs.litellm.ai/docs/providers",target:"_blank",icon:(0,t.jsx)(C.InfoCircleOutlined,{}),children:"View Documentation"})})]})},ta=async(e,t,s,a)=>{try{let r;"complexity_router"===e.model_type?r={model_name:e.auto_router_name,litellm_params:{model:"auto_router/complexity_router",complexity_router_config:e.complexity_router_config,complexity_router_default_model:e.auto_router_default_model},model_info:{}}:(r={model_name:e.auto_router_name,litellm_params:{model:`auto_router/${e.auto_router_name}`,auto_router_config:JSON.stringify(e.auto_router_config),auto_router_default_model:e.auto_router_default_model},model_info:{}},e.auto_router_embedding_model&&"custom"!==e.auto_router_embedding_model?r.litellm_params.auto_router_embedding_model=e.auto_router_embedding_model:e.custom_embedding_model&&(r.litellm_params.auto_router_embedding_model=e.custom_embedding_model)),e.team_id&&(r.model_info.team_id=e.team_id),e.model_access_group&&e.model_access_group.length>0&&(r.model_info.access_groups=e.model_access_group),await (0,l.modelCreateCall)(t,r);let i="complexity_router"===e.model_type?"Complexity Router":"Semantic Router";G.default.success(`Successfully created ${i}: ${e.auto_router_name}`),s.resetFields(),a&&a()}catch(e){console.error("Failed to add auto router:",e),G.default.fromBackend("Failed to add auto router: "+e)}};var tr=e.i(695411),ti=e.i(955135),to=e.i(646563),tn=e.i(362024),td=e.i(21548);let{Text:tc}=R.Typography,{TextArea:tm}=eV.Input,tu=({modelInfo:e,value:l,onChange:s})=>{let[a,r]=(0,x.useState)([]),[i,o]=(0,x.useState)(!1),[n,d]=(0,x.useState)([]);(0,x.useEffect)(()=>{let e=l?.routes;if(e){let t=[];r(l=>e.map((e,s)=>{let a=l[s],r=a?.id||e.id||`route-${s}-${Date.now()}`;return t.push(r),{id:r,model:e.name||e.model||"",utterances:e.utterances||[],description:e.description||"",score_threshold:e.score_threshold??.5}})),d(t)}else r([]),d([])},[l]);let c=(e,t,l)=>{let s=a.map(s=>s.id===e?{...s,[t]:l}:s);r(s),m(s)},m=e=>{let t={routes:e.map(e=>({name:e.model,utterances:e.utterances,description:e.description,score_threshold:e.score_threshold}))};s?.(t)},u=e.map(e=>({value:e.model_group,label:e.model_group}));return(0,t.jsxs)("div",{className:"w-full max-w-none",children:[(0,t.jsxs)(M.Flex,{justify:"space-between",align:"center",gap:"middle",style:{width:"100%",marginBottom:24},children:[(0,t.jsxs)(E.Space,{align:"center",children:[(0,t.jsx)(R.Typography.Title,{level:4,style:{margin:0},children:"Routes Configuration"}),(0,t.jsx)(O.Tooltip,{title:"Configure routing logic to automatically select the best model based on user input patterns",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"text-gray-400"})})]}),(0,t.jsx)(Q.Button,{type:"primary",icon:(0,t.jsx)(to.PlusOutlined,{}),onClick:()=>{let e=`route-${Date.now()}`,t=[...a,{id:e,model:"",utterances:[],description:"",score_threshold:.5}];r(t),m(t),d(t=>[...t,e])},className:"bg-blue-600 hover:bg-blue-700",children:"Add Route"})]}),0===a.length?(0,t.jsx)(e_.Card,{children:(0,t.jsx)(td.Empty,{description:'No routes configured. Click "Add Route" to get started.'})}):(0,t.jsx)(tn.Collapse,{activeKey:n,onChange:e=>d(Array.isArray(e)?e:[e].filter(Boolean)),style:{width:"100%"},items:a.map((e,l)=>({key:e.id,label:(0,t.jsxs)(tc,{style:{fontSize:16},children:["Route ",l+1,": ",e.model||"Unnamed"]}),extra:(0,t.jsx)(Q.Button,{type:"text",danger:!0,size:"small",icon:(0,t.jsx)(ti.DeleteOutlined,{}),onClick:t=>{var l;let s;t.stopPropagation(),l=e.id,r(s=a.filter(e=>e.id!==l)),m(s),d(e=>e.filter(e=>e!==l))}}),children:(0,t.jsxs)(e_.Card,{children:[(0,t.jsxs)("div",{className:"mb-4 w-full",children:[(0,t.jsx)(tc,{className:"text-sm font-medium mb-2 block",children:"Model"}),(0,t.jsx)(Y.Select,{value:e.model,onChange:t=>c(e.id,"model",t),placeholder:"Select model",showSearch:!0,style:{width:"100%"},options:u})]}),(0,t.jsxs)("div",{className:"mb-4 w-full",children:[(0,t.jsx)(tc,{className:"text-sm font-medium mb-2 block",children:"Description"}),(0,t.jsx)(tm,{value:e.description,onChange:t=>c(e.id,"description",t.target.value),placeholder:"Describe when this route should be used...",rows:2,style:{width:"100%"}})]}),(0,t.jsxs)("div",{className:"mb-4 w-full",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-2",children:[(0,t.jsx)(tc,{className:"text-sm font-medium",children:"Score Threshold"}),(0,t.jsx)(O.Tooltip,{title:"Minimum similarity score to route to this model (0-1)",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"text-gray-400"})})]}),(0,t.jsx)(eh.InputNumber,{value:e.score_threshold,onChange:t=>c(e.id,"score_threshold",t||0),min:0,max:1,step:.1,style:{width:"100%"},placeholder:"0.5"})]}),(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-2",children:[(0,t.jsx)(tc,{className:"text-sm font-medium",children:"Example Utterances"}),(0,t.jsx)(O.Tooltip,{title:"Training examples for this route. Type an utterance and press Enter to add it.",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"text-gray-400"})})]}),(0,t.jsx)(tc,{className:"text-xs text-gray-500 mb-2",children:"Type an utterance and press Enter to add it. You can also paste multiple lines."}),(0,t.jsx)(Y.Select,{mode:"tags",value:e.utterances,onChange:t=>c(e.id,"utterances",t),placeholder:"Type an utterance and press Enter...",style:{width:"100%"},tokenSeparators:["\n"],maxTagCount:"responsive",allowClear:!0})]})]},e.id)}))}),(0,t.jsx)(P.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4 w-full",children:[(0,t.jsx)(tc,{className:"text-lg font-semibold",children:"JSON Preview"}),(0,t.jsx)(Q.Button,{type:"link",onClick:()=>o(!i),className:"text-blue-600 p-0",children:i?"Hide":"Show"})]}),i&&(0,t.jsx)(e_.Card,{className:"bg-gray-50 w-full",children:(0,t.jsx)("pre",{className:"text-sm overflow-auto max-h-64 w-full",children:JSON.stringify({routes:a.map(e=>({name:e.model,utterances:e.utterances,description:e.description,score_threshold:e.score_threshold}))},null,2)})})]})},{Text:th}=R.Typography,tp={SIMPLE:{label:"Simple",description:"Basic questions, greetings, simple factual queries",examples:'"Hello!", "What is Python?", "Thanks!"'},MEDIUM:{label:"Medium",description:"Standard queries requiring some reasoning or explanation",examples:'"Explain how REST APIs work", "Debug this error"'},COMPLEX:{label:"Complex",description:"Technical, multi-part requests requiring deep knowledge",examples:'"Design a microservices architecture", "Implement a rate limiter"'},REASONING:{label:"Reasoning",description:"Chain-of-thought, analysis, explicit reasoning requests",examples:'"Think step by step...", "Analyze the pros and cons..."'}},tx=({modelInfo:e,value:l,onChange:s})=>{let a=e.map(e=>({value:e.model_group,label:e.model_group}));return(0,t.jsxs)("div",{className:"w-full max-w-none",children:[(0,t.jsxs)(E.Space,{align:"center",style:{marginBottom:16},children:[(0,t.jsx)(R.Typography.Title,{level:4,style:{margin:0},children:"Complexity Tier Configuration"}),(0,t.jsx)(O.Tooltip,{title:"Map each complexity tier to a model. Simple queries use cheaper/faster models, complex queries use more capable models.",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"text-gray-400"})})]}),(0,t.jsx)(th,{type:"secondary",style:{display:"block",marginBottom:24},children:"The complexity router automatically classifies requests by complexity using rule-based scoring (no API calls, <1ms latency). Configure which model handles each tier."}),(0,t.jsx)(e_.Card,{children:Object.keys(tp).map((e,r)=>{let i=tp[e];return(0,t.jsxs)("div",{children:[r>0&&(0,t.jsx)(P.Divider,{style:{margin:"16px 0"}}),(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-2",children:[(0,t.jsxs)(th,{strong:!0,style:{fontSize:16},children:[i.label," Tier"]}),(0,t.jsx)(O.Tooltip,{title:i.description,children:(0,t.jsx)(C.InfoCircleOutlined,{className:"text-gray-400"})})]}),(0,t.jsxs)(th,{type:"secondary",style:{display:"block",marginBottom:8,fontSize:12},children:["Examples: ",i.examples]}),(0,t.jsx)(Y.Select,{value:l[e],onChange:t=>{s({...l,[e]:t})},placeholder:`Select model for ${i.label.toLowerCase()} queries`,showSearch:!0,style:{width:"100%"},options:a})]})]},e)})}),(0,t.jsx)(P.Divider,{}),(0,t.jsxs)(e_.Card,{className:"bg-gray-50",children:[(0,t.jsx)(th,{strong:!0,style:{display:"block",marginBottom:8},children:"How Classification Works"}),(0,t.jsx)(th,{type:"secondary",style:{fontSize:13},children:"The router scores each request across 7 dimensions: token count, code presence, reasoning markers, technical terms, simple indicators, multi-step patterns, and question complexity. The weighted score determines the tier:"}),(0,t.jsxs)("ul",{style:{marginTop:8,marginBottom:0,paddingLeft:20,fontSize:13,color:"rgba(0, 0, 0, 0.45)"},children:[(0,t.jsxs)("li",{children:[(0,t.jsx)("strong",{children:"SIMPLE"}),": Score < 0.15"]}),(0,t.jsxs)("li",{children:[(0,t.jsx)("strong",{children:"MEDIUM"}),": Score 0.15 - 0.35"]}),(0,t.jsxs)("li",{children:[(0,t.jsx)("strong",{children:"COMPLEX"}),": Score 0.35 - 0.60"]}),(0,t.jsxs)("li",{children:[(0,t.jsx)("strong",{children:"REASONING"}),": Score > 0.60 (or 2+ reasoning markers)"]})]})]})]})};var tg=e.i(962944),tf=e.i(539677);let{Title:t_,Link:tj}=R.Typography,ty=({form:e,handleOk:s,accessToken:a,userRole:r})=>{let[i,o]=(0,x.useState)(!1),[n,d]=(0,x.useState)(!1),[c,m]=(0,x.useState)(""),[u,h]=(0,x.useState)([]),[p,g]=(0,x.useState)([]),[f,_]=(0,x.useState)(!1),[j,y]=(0,x.useState)(!1),[b,v]=(0,x.useState)("complexity"),[N,w]=(0,x.useState)(null),[C,k]=(0,x.useState)({SIMPLE:"",MEDIUM:"",COMPLEX:"",REASONING:""});(0,x.useEffect)(()=>{(async()=>{h((await (0,l.modelAvailableCall)(a,"","",!1,null,!0,!0)).data.map(e=>e.id))})()},[a]),(0,x.useEffect)(()=>{(async()=>{try{let e=await (0,tr.fetchAvailableModels)(a);g(e)}catch(e){console.error("Error fetching model info for auto router:",e)}})()},[a]);let S=e0.all_admin_roles.includes(r),T=async()=>{d(!0),m(`test-${Date.now()}`),o(!0)},I=()=>{let t=e.getFieldsValue();if(!t.auto_router_name)return void G.default.fromBackend("Please enter an Auto Router Name");if("complexity"===b){if(0===Object.values(C).filter(Boolean).length)return void G.default.fromBackend("Please select at least one model for a complexity tier");let l=C.MEDIUM||C.SIMPLE||C.COMPLEX||C.REASONING;e.setFieldsValue({custom_llm_provider:"auto_router",model:t.auto_router_name,api_key:"not_required_for_auto_router",auto_router_default_model:l}),e.validateFields(["auto_router_name"]).then(r=>{ta({...r,auto_router_name:t.auto_router_name,auto_router_default_model:l,model_type:"complexity_router",complexity_router_config:{tiers:C},model_access_group:t.model_access_group},a,e,s)}).catch(e=>{console.error("Validation failed:",e),G.default.fromBackend("Please fill in all required fields")})}else{if(!t.auto_router_default_model)return void G.default.fromBackend("Please select a Default Model");if(e.setFieldsValue({custom_llm_provider:"auto_router",model:t.auto_router_name,api_key:"not_required_for_auto_router"}),!N||!N.routes||0===N.routes.length)return void G.default.fromBackend("Please configure at least one route for the auto router");if(N.routes.filter(e=>!e.name||!e.description||0===e.utterances.length).length>0)return void G.default.fromBackend("Please ensure all routes have a target model, description, and at least one utterance");e.validateFields().then(t=>{ta({...t,auto_router_config:N,model_type:"semantic_router"},a,e,s)}).catch(e=>{console.error("Validation failed:",e);let t=e.errorFields||[];if(t.length>0){let e=t.map(e=>{let t=e.name[0];return({auto_router_name:"Auto Router Name",auto_router_default_model:"Default Model",auto_router_embedding_model:"Embedding Model"})[t]||t});G.default.fromBackend(`Please fill in the following required fields: ${e.join(", ")}`)}else G.default.fromBackend("Please fill in all required fields")})}};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(t_,{level:2,children:"Add Auto Router"}),(0,t.jsx)(em.Text,{className:"text-gray-600 mb-6",children:"Create an auto router that automatically selects the best model based on request complexity or semantic matching."}),(0,t.jsx)(e_.Card,{className:"mb-4",children:(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(em.Text,{className:"text-sm font-medium mb-2 block",children:"Router Type"}),(0,t.jsx)(e9.Radio.Group,{value:b,onChange:e=>v(e.target.value),className:"w-full",children:(0,t.jsxs)(E.Space,{direction:"vertical",className:"w-full",children:[(0,t.jsxs)(e9.Radio,{value:"complexity",className:"w-full",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(tg.ThunderboltOutlined,{className:"text-yellow-500"}),(0,t.jsx)("span",{className:"font-medium",children:"Complexity Router"}),(0,t.jsx)(W.Badge,{count:"Recommended",style:{backgroundColor:"#52c41a",fontSize:"10px",padding:"0 6px"}})]}),(0,t.jsxs)("div",{className:"text-xs text-gray-500 ml-6 mt-1",children:["Automatically routes based on request complexity. No training data needed — just pick 4 models and go.",(0,t.jsx)("br",{}),(0,t.jsx)("span",{className:"text-green-600",children:"✓ Zero API calls"})," ·"," ",(0,t.jsx)("span",{className:"text-green-600",children:"✓ <1ms latency"})," ·"," ",(0,t.jsx)("span",{className:"text-green-600",children:"✓ No cost"})]})]}),(0,t.jsxs)(e9.Radio,{value:"semantic",className:"w-full mt-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(tf.BranchesOutlined,{className:"text-blue-500"}),(0,t.jsx)("span",{className:"font-medium",children:"Semantic Router"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500 ml-6 mt-1",children:"Routes based on semantic similarity to example utterances. Requires embedding model and training examples."})]})]})})]})}),(0,t.jsx)(e_.Card,{children:(0,t.jsxs)(el.Form,{form:e,onFinish:I,labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsx)(el.Form.Item,{rules:[{required:!0,message:"Auto router name is required"}],label:"Auto Router Name",name:"auto_router_name",tooltip:"Unique name for this auto router configuration",labelCol:{span:10},labelAlign:"left",children:(0,t.jsx)(eO.TextInput,{placeholder:"e.g., smart_router, auto_router_1"})}),"complexity"===b?(0,t.jsx)("div",{className:"w-full mb-4",children:(0,t.jsx)(tx,{modelInfo:p,value:C,onChange:e=>{k(e)}})}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"w-full mb-4",children:(0,t.jsx)(tu,{modelInfo:p,value:N,onChange:t=>{w(t),e.setFieldValue("auto_router_config",t)}})}),(0,t.jsx)(el.Form.Item,{rules:[{required:"semantic"===b,message:"Default model is required"}],label:"Default Model",name:"auto_router_default_model",tooltip:"Fallback model to use when auto routing logic cannot determine the best model",labelCol:{span:10},labelAlign:"left",children:(0,t.jsx)(Y.Select,{placeholder:"Select a default model",onChange:e=>{_("custom"===e)},options:[...Array.from(new Set(p.map(e=>e.model_group))).map(e=>({value:e,label:e})),{value:"custom",label:"Enter custom model name"}],style:{width:"100%"},showSearch:!0})}),(0,t.jsx)(el.Form.Item,{label:"Embedding Model",name:"auto_router_embedding_model",tooltip:"Optional: Embedding model to use for semantic routing decisions",labelCol:{span:10},labelAlign:"left",children:(0,t.jsx)(Y.Select,{value:e.getFieldValue("auto_router_embedding_model"),placeholder:"Select an embedding model (optional)",onChange:t=>{y("custom"===t),e.setFieldValue("auto_router_embedding_model",t)},options:[...Array.from(new Set(p.map(e=>e.model_group))).map(e=>({value:e,label:e})),{value:"custom",label:"Enter custom model name"}],style:{width:"100%"},showSearch:!0,allowClear:!0})})]}),(0,t.jsxs)("div",{className:"flex items-center my-4",children:[(0,t.jsx)("div",{className:"grow border-t border-gray-200"}),(0,t.jsx)("span",{className:"px-4 text-gray-500 text-sm",children:"Additional Settings"}),(0,t.jsx)("div",{className:"grow border-t border-gray-200"})]}),S&&(0,t.jsx)(el.Form.Item,{label:"Model Access Group",name:"model_access_group",className:"mb-4",tooltip:"Use model access groups to control who can access this auto router",children:(0,t.jsx)(Y.Select,{mode:"tags",showSearch:!0,placeholder:"Select existing groups or type to create new ones",optionFilterProp:"children",tokenSeparators:[","],options:u.map(e=>({value:e,label:e})),maxTagCount:"responsive",allowClear:!0})}),(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(O.Tooltip,{title:"Get help on our github",children:(0,t.jsx)(R.Typography.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})}),(0,t.jsxs)("div",{className:"space-x-2",children:[(0,t.jsx)(Q.Button,{onClick:T,loading:n,children:"Test Connection"}),(0,t.jsx)(Q.Button,{type:"primary",onClick:()=>{I()},children:"Add Auto Router"})]})]})]})}),(0,t.jsx)(es.Modal,{title:"Connection Test Results",open:i,onCancel:()=>{o(!1),d(!1)},footer:[(0,t.jsx)(Q.Button,{onClick:()=>{o(!1),d(!1)},children:"Close"},"close")],width:700,children:i&&(0,t.jsx)(ts,{formValues:e.getFieldsValue(),accessToken:a,testMode:"chat",modelName:e.getFieldValue("auto_router_name"),onClose:()=>{o(!1),d(!1)},onTestComplete:()=>d(!1)},c)})]})};var tb=e.i(838932),tv=e.i(109034),tN=e.i(793130),tw=e.i(560445),tC=e.i(663435),tk=e.i(677667),tS=e.i(898667),tT=e.i(130643),tI=e.i(635432),tF=e.i(564897),tP=e.i(435451);let{Text:tM}=R.Typography,tA=({form:e,showCacheControl:l,onCacheControlChange:s})=>{let a=t=>{let l=e.getFieldValue("litellm_extra_params");try{let s=l?JSON.parse(l):{};t.length>0?s.cache_control_injection_points=t:delete s.cache_control_injection_points,Object.keys(s).length>0?e.setFieldValue("litellm_extra_params",JSON.stringify(s,null,2)):e.setFieldValue("litellm_extra_params","")}catch(e){console.error("Error updating cache control points:",e)}};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(el.Form.Item,{label:"Cache Control Injection Points",name:"cache_control",valuePropName:"checked",className:"mb-4",tooltip:"Tell litellm where to inject cache control checkpoints. You can specify either by role (to apply to all messages of that role) or by specific message index.",children:(0,t.jsx)(L.Switch,{onChange:s,className:"bg-gray-600"})}),l&&(0,t.jsxs)("div",{className:"ml-6 pl-4 border-l-2 border-gray-200",children:[(0,t.jsx)(tM,{className:"text-sm text-gray-500 block mb-4",children:"Providers like Anthropic, Bedrock API require users to specify where to inject cache control checkpoints, litellm can automatically add them for you as a cost saving feature."}),(0,t.jsx)(el.Form.List,{name:"cache_control_injection_points",initialValue:[{location:"message"}],children:(l,{add:s,remove:r})=>(0,t.jsxs)(t.Fragment,{children:[l.map((s,i)=>(0,t.jsxs)("div",{className:"flex items-center mb-4 gap-4",children:[(0,t.jsx)(el.Form.Item,{...s,label:"Type",name:[s.name,"location"],initialValue:"message",className:"mb-0",style:{width:"180px"},children:(0,t.jsx)(Y.Select,{disabled:!0,options:[{value:"message",label:"Message"}]})}),(0,t.jsx)(el.Form.Item,{...s,label:"Role",name:[s.name,"role"],className:"mb-0",style:{width:"180px"},tooltip:"LiteLLM will mark all messages of this role as cacheable",children:(0,t.jsx)(Y.Select,{placeholder:"Select a role",allowClear:!0,options:[{value:"user",label:"User"},{value:"system",label:"System"},{value:"assistant",label:"Assistant"}],onChange:()=>{a(e.getFieldValue("cache_control_points"))}})}),(0,t.jsx)(el.Form.Item,{...s,label:"Index",name:[s.name,"index"],className:"mb-0",style:{width:"180px"},tooltip:"(Optional) If set litellm will mark the message at this index as cacheable",children:(0,t.jsx)(tP.default,{type:"number",placeholder:"Optional",step:1,onChange:()=>{a(e.getFieldValue("cache_control_points"))}})}),l.length>1&&(0,t.jsx)(tF.MinusCircleOutlined,{className:"text-red-500 cursor-pointer text-lg ml-12",onClick:()=>{r(s.name),setTimeout(()=>{a(e.getFieldValue("cache_control_points"))},0)}})]},s.key)),(0,t.jsx)(el.Form.Item,{children:(0,t.jsxs)("button",{type:"button",className:"flex items-center justify-center w-full border border-dashed border-gray-300 py-2 px-4 text-gray-600 hover:text-blue-600 hover:border-blue-300 transition-all rounded-sm",onClick:()=>s(),children:[(0,t.jsx)(to.PlusOutlined,{className:"mr-2"}),"Add Injection Point"]})})]})})]})]})};var tE=e.i(916940),tL=e.i(122550);let{Link:tO}=R.Typography,tR=({showAdvancedSettings:e,setShowAdvancedSettings:l,teams:s,guardrailsList:a,tagsList:r,accessToken:i})=>{let[o]=el.Form.useForm(),[n,d]=x.default.useState(!1),[c,m]=x.default.useState("per_token"),[u,h]=x.default.useState(!1),p=(e,t)=>t&&(isNaN(Number(t))||0>Number(t))?Promise.reject("Please enter a valid positive number"):Promise.resolve();return(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)(tk.Accordion,{className:"mt-2 mb-4",children:[(0,t.jsx)(tS.AccordionHeader,{children:(0,t.jsx)("b",{children:"Advanced Settings"})}),(0,t.jsx)(tT.AccordionBody,{children:(0,t.jsxs)("div",{className:"bg-white rounded-lg",children:[(0,t.jsx)(el.Form.Item,{label:"Custom Pricing",name:"custom_pricing",valuePropName:"checked",className:"mb-4",children:(0,t.jsx)(L.Switch,{onChange:e=>{d(e),e||o.setFieldsValue({input_cost_per_token:void 0,output_cost_per_token:void 0,cache_read_input_token_cost:void 0,cache_creation_input_token_cost:void 0,input_cost_per_second:void 0})},className:"bg-gray-600"})}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{children:["Attached Knowledge Bases (RAG)"," ",(0,t.jsx)(O.Tooltip,{title:"Vector stores to use for RAG. Every request to this model will automatically retrieve context from these knowledge bases.",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/completion/knowledgebase",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(C.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"vector_store_ids",className:"mt-4",help:"Select vector stores to attach. Requests to this model will automatically use these for RAG. Set up vector stores in Tools > Vector Stores.",children:(0,t.jsx)(tE.default,{onChange:()=>{},accessToken:i,placeholder:"Select knowledge bases (optional)"})}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(O.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(C.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:"Select existing guardrails. Go to 'Guardrails' tab to create new guardrails.",children:(0,t.jsx)(Y.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter guardrails",options:a.map(e=>({value:e,label:e}))})}),(0,t.jsx)(el.Form.Item,{label:"Tags",name:"tags",className:"mb-4",children:(0,t.jsx)(Y.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",options:Object.values(r).map(e=>({value:e.name,label:e.name,title:e.description||e.name}))})}),n&&(0,t.jsxs)("div",{className:"ml-6 pl-4 border-l-2 border-gray-200",children:[(0,t.jsx)(el.Form.Item,{label:"Pricing Model",name:"pricing_model",className:"mb-4",children:(0,t.jsx)(Y.Select,{defaultValue:"per_token",onChange:e=>m(e),options:[{value:"per_token",label:"Per Million Tokens"},{value:"per_second",label:"Per Second"}]})}),"per_token"===c?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(el.Form.Item,{label:"Input Cost (per 1M tokens)",name:"input_cost_per_token",rules:[{validator:p}],className:"mb-4",children:(0,t.jsx)(eO.TextInput,{})}),(0,t.jsx)(el.Form.Item,{label:"Output Cost (per 1M tokens)",name:"output_cost_per_token",rules:[{validator:p}],className:"mb-4",children:(0,t.jsx)(eO.TextInput,{})}),(0,t.jsx)(el.Form.Item,{label:"Cache Read Cost (per 1M tokens)",name:"cache_read_input_token_cost",rules:[{validator:p}],tooltip:"If left blank, defaults to Input Cost.",className:"mb-4",children:(0,t.jsx)(eO.TextInput,{placeholder:"Defaults to Input Cost if blank"})}),(0,t.jsx)(el.Form.Item,{label:"Cache Write Cost (per 1M tokens)",name:"cache_creation_input_token_cost",rules:[{validator:p}],tooltip:"If left blank, defaults to Input Cost (the backend falls back to input_cost_per_token when no cache-write rate is set).",className:"mb-4",children:(0,t.jsx)(eO.TextInput,{placeholder:"Defaults to Input Cost if blank"})})]}):(0,t.jsx)(el.Form.Item,{label:"Cost Per Second",name:"input_cost_per_second",rules:[{validator:p}],className:"mb-4",children:(0,t.jsx)(eO.TextInput,{})})]}),(0,t.jsx)(el.Form.Item,{label:"Use in pass through routes",name:"use_in_pass_through",valuePropName:"checked",className:"mb-4 mt-4",tooltip:(0,t.jsxs)("span",{children:["Allow using these credentials in pass through routes."," ",(0,t.jsx)(tO,{href:"https://docs.litellm.ai/docs/pass_through/vertex_ai",target:"_blank",children:"Learn more"})]}),children:(0,t.jsx)(L.Switch,{onChange:e=>{let t=o.getFieldValue("litellm_extra_params");try{let l=t?JSON.parse(t):{};e?l.use_in_pass_through=!0:delete l.use_in_pass_through,Object.keys(l).length>0?o.setFieldValue("litellm_extra_params",JSON.stringify(l,null,2)):o.setFieldValue("litellm_extra_params","")}catch(t){e?o.setFieldValue("litellm_extra_params",JSON.stringify({use_in_pass_through:!0},null,2)):o.setFieldValue("litellm_extra_params","")}},className:"bg-gray-600"})}),(0,t.jsx)(tA,{form:o,showCacheControl:u,onCacheControlChange:e=>{if(h(e),!e){let e=o.getFieldValue("litellm_extra_params");try{let t=e?JSON.parse(e):{};delete t.cache_control_injection_points,Object.keys(t).length>0?o.setFieldValue("litellm_extra_params",JSON.stringify(t,null,2)):o.setFieldValue("litellm_extra_params","")}catch(e){o.setFieldValue("litellm_extra_params","")}}}}),(0,t.jsx)(el.Form.Item,{label:"LiteLLM Params",name:"litellm_extra_params",tooltip:"Optional litellm params used for making a litellm.completion() call.",className:"mb-4 mt-4",rules:[{validator:tL.formItemValidateJSON}],children:(0,t.jsx)(tI.default,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,t.jsxs)(eD.Row,{className:"mb-4",children:[(0,t.jsx)(eq.Col,{span:10}),(0,t.jsx)(eq.Col,{span:10,children:(0,t.jsxs)(em.Text,{className:"text-gray-600 text-sm",children:["Pass JSON of litellm supported params"," ",(0,t.jsx)(tO,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]}),(0,t.jsx)(el.Form.Item,{label:"Model Info",name:"model_info_params",tooltip:"Optional model info params. Returned when calling `/model/info` endpoint.",className:"mb-0",rules:[{validator:tL.formItemValidateJSON}],children:(0,t.jsx)(tI.default,{rows:4,placeholder:'{ "mode": "chat" }'})})]})})]})})};var tB=e.i(291542),tz=e.i(750113);let tq=({content:e,children:l,width:s="auto",className:a=""})=>{let[r,i]=(0,x.useState)(!1),[o,n]=(0,x.useState)("top"),d=(0,x.useRef)(null);return(0,t.jsxs)("div",{className:"relative inline-block",ref:d,children:[l||(0,t.jsx)(tz.QuestionCircleOutlined,{className:"ml-1 text-gray-500 cursor-help",onMouseEnter:()=>{if(d.current){let e=d.current.getBoundingClientRect(),t=e.top,l=window.innerHeight-e.bottom;t<300&&l>300?n("bottom"):n("top")}i(!0)},onMouseLeave:()=>i(!1)}),r&&(0,t.jsxs)("div",{className:`absolute left-1/2 -translate-x-1/2 z-50 bg-black/90 text-white p-2 rounded-md text-sm font-normal shadow-lg ${a}`,style:{["top"===o?"bottom":"top"]:"100%",width:s,marginBottom:"top"===o?"8px":"0",marginTop:"bottom"===o?"8px":"0"},children:[e,(0,t.jsx)("div",{className:"absolute left-1/2 -translate-x-1/2 w-0 h-0",style:{top:"top"===o?"100%":"auto",bottom:"bottom"===o?"100%":"auto",borderTop:"top"===o?"6px solid rgba(0, 0, 0, 0.9)":"6px solid transparent",borderBottom:"bottom"===o?"6px solid rgba(0, 0, 0, 0.9)":"6px solid transparent",borderLeft:"6px solid transparent",borderRight:"6px solid transparent"}})]})]})},tV=()=>{let e=el.Form.useFormInstance(),[l,s]=(0,x.useState)(0),a=el.Form.useWatch("model",e)||[],r=Array.isArray(a)?a:[a],i=el.Form.useWatch("custom_model_name",e),o=!r.includes("all-wildcard"),n=el.Form.useWatch("custom_llm_provider",e);if((0,x.useEffect)(()=>{if(i&&r.includes("custom")){let t=(e.getFieldValue("model_mappings")||[]).map(e=>"custom"===e.public_name||"custom"===e.litellm_model?n===eP.Providers.Azure?{public_name:i,litellm_model:`azure/${i}`}:{public_name:i,litellm_model:i}:e);e.setFieldValue("model_mappings",t),s(e=>e+1)}},[i,r,n,e]),(0,x.useEffect)(()=>{if(r.length>0&&!r.includes("all-wildcard")){let t=e.getFieldValue("model_mappings")||[];if(t.length!==r.length||!r.every(e=>t.some(t=>"custom"===e?"custom"===t.litellm_model||t.litellm_model===i:n===eP.Providers.Azure?t.litellm_model===`azure/${e}`:t.litellm_model===e))){let t=r.map(e=>"custom"===e&&i?n===eP.Providers.Azure?{public_name:i,litellm_model:`azure/${i}`}:{public_name:i,litellm_model:i}:n===eP.Providers.Azure?{public_name:e,litellm_model:`azure/${e}`}:{public_name:e,litellm_model:e});e.setFieldValue("model_mappings",t),s(e=>e+1)}}},[r,i,n,e]),!o)return null;let d=(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"mb-2 font-normal",children:"The name you specify in your API calls to LiteLLM Proxy"}),(0,t.jsxs)("div",{className:"mb-2 font-normal",children:[(0,t.jsx)("strong",{children:"Example:"})," If you name your public model"," ",(0,t.jsx)("code",{className:"bg-gray-700 px-1 py-0.5 rounded-sm text-xs",children:"example-name"}),", and choose"," ",(0,t.jsx)("code",{className:"bg-gray-700 px-1 py-0.5 rounded-sm text-xs",children:"openai/qwen-plus-latest"})," as the LiteLLM model"]}),(0,t.jsxs)("div",{className:"mb-2 font-normal",children:[(0,t.jsx)("strong",{children:"Usage:"})," You make an API call to the LiteLLM proxy with"," ",(0,t.jsx)("code",{className:"bg-gray-700 px-1 py-0.5 rounded-sm text-xs",children:'model = "example-name"'})]}),(0,t.jsxs)("div",{className:"font-normal",children:[(0,t.jsx)("strong",{children:"Result:"})," LiteLLM sends"," ",(0,t.jsx)("code",{className:"bg-gray-700 px-1 py-0.5 rounded-sm text-xs",children:"qwen-plus-latest"})," to the provider"]})]}),c=(0,t.jsx)("div",{children:"The model name LiteLLM will send to the LLM API"}),m=[{title:(0,t.jsxs)("span",{className:"flex items-center",children:["Public Model Name",(0,t.jsx)(tq,{content:d,width:"500px"})]}),dataIndex:"public_name",key:"public_name",render:(l,s,a)=>(0,t.jsx)(eO.TextInput,{value:l,onChange:t=>{let l=t.target.value,s=[...e.getFieldValue("model_mappings")],r=n===eP.Providers.Anthropic,i=l.endsWith("-1m"),o=e.getFieldValue("litellm_extra_params"),d=!o||""===o.trim(),c=l;if(r&&i&&d){let t=JSON.stringify({extra_headers:{"anthropic-beta":"context-1m-2025-08-07"}},null,2);e.setFieldValue("litellm_extra_params",t),c=l.slice(0,-3)}s[a].public_name=c,e.setFieldValue("model_mappings",s)}})},{title:(0,t.jsxs)("span",{className:"flex items-center",children:["LiteLLM Model Name",(0,t.jsx)(tq,{content:c,width:"360px"})]}),dataIndex:"litellm_model",key:"litellm_model"}];return(0,t.jsx)(t.Fragment,{children:(0,t.jsx)(el.Form.Item,{label:"Model Mappings",name:"model_mappings",tooltip:"Map public model names to LiteLLM model names for load balancing",labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",rules:[{required:!0,validator:async(e,t)=>{if(!t||0===t.length)throw Error("At least one model mapping is required");if(t.filter(e=>!e.public_name||""===e.public_name.trim()).length>0)throw Error("All model mappings must have valid public names")}}],children:(0,t.jsx)(tB.Table,{dataSource:e.getFieldValue("model_mappings"),columns:m,pagination:!1,size:"small"},l)})})},tD=({selectedProvider:e,providerModels:l,getPlaceholder:s})=>{let a=el.Form.useFormInstance(),r=t=>{let l=t.target.value,s=(a.getFieldValue("model_mappings")||[]).map(t=>"custom"===t.public_name||"custom"===t.litellm_model?e===eP.Providers.Azure?{public_name:l,litellm_model:`azure/${l}`}:{public_name:l,litellm_model:l}:t);a.setFieldsValue({model_mappings:s})};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(el.Form.Item,{label:"LiteLLM Model Name(s)",tooltip:"The model name LiteLLM will send to the LLM API",className:"mb-0",children:[(0,t.jsx)(el.Form.Item,{name:"model",rules:[{required:!0,message:`Please enter ${e===eP.Providers.Azure?"a deployment name":"at least one model"}.`}],noStyle:!0,children:e===eP.Providers.Azure||e===eP.Providers.OpenAI_Compatible||e===eP.Providers.Ollama?(0,t.jsx)(t.Fragment,{children:(0,t.jsx)(eO.TextInput,{placeholder:s(e),onChange:e===eP.Providers.Azure?e=>{let t=e.target.value,l=t?[{public_name:t,litellm_model:`azure/${t}`}]:[];a.setFieldsValue({model:t,model_mappings:l})}:void 0})}):l.length>0?(0,t.jsx)(Y.Select,{"data-testid":"model-name-select",mode:"multiple",allowClear:!0,showSearch:!0,placeholder:"Select models",onChange:t=>{let l=Array.isArray(t)?t:[t];if(l.includes("all-wildcard"))a.setFieldsValue({model_name:void 0,model_mappings:[]});else if(JSON.stringify(a.getFieldValue("model"))!==JSON.stringify(l)){let t=l.map(t=>e===eP.Providers.Azure?{public_name:t,litellm_model:`azure/${t}`}:{public_name:t,litellm_model:t});a.setFieldsValue({model:l,model_mappings:t})}},optionFilterProp:"children",filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:[{label:"Custom Model Name (Enter below)",value:"custom"},{label:`All ${e} Models (Wildcard)`,value:"all-wildcard"},...l.map(e=>({label:e,value:e}))],style:{width:"100%"}}):(0,t.jsx)(eO.TextInput,{placeholder:s(e)})}),(0,t.jsx)(el.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.model!==t.model,children:({getFieldValue:l})=>{let s=l("model")||[];return(Array.isArray(s)?s:[s]).includes("custom")&&(0,t.jsx)(el.Form.Item,{name:"custom_model_name",rules:[{required:!0,message:"Please enter a custom model name."}],className:"mt-2",children:(0,t.jsx)(eO.TextInput,{placeholder:e===eP.Providers.Azure?"Enter Azure deployment name":"Enter custom model name",onChange:r})})}})]}),(0,t.jsxs)(eD.Row,{children:[(0,t.jsx)(eq.Col,{span:10}),(0,t.jsx)(eq.Col,{span:14,children:(0,t.jsx)(em.Text,{className:"mb-3 mt-1",children:e===eP.Providers.Azure?"Your deployment name will be saved as the public model name, and LiteLLM will use 'azure/deployment-name' internally":"The model name LiteLLM will send to the LLM API"})})]})]})},tH=[{value:"chat",label:"Chat - /chat/completions"},{value:"completion",label:"Completion - /completions"},{value:"embedding",label:"Embedding - /embeddings"},{value:"audio_speech",label:"Audio Speech - /audio/speech"},{value:"audio_transcription",label:"Audio Transcription - /audio/transcriptions"},{value:"image_generation",label:"Image Generation - /images/generations"},{value:"video_generation",label:"Video Generation - /videos"},{value:"rerank",label:"Rerank - /rerank"},{value:"realtime",label:"Realtime - /realtime"},{value:"batch",label:"Batch - /batch"},{value:"ocr",label:"OCR - /ocr"}],{Title:tG,Link:tU}=R.Typography,t$=({form:e,handleOk:s,selectedProvider:a,setSelectedProvider:i,providerModels:o,setProviderModelsFn:n,getPlaceholder:d,uploadProps:c,showAdvancedSettings:m,setShowAdvancedSettings:u,teams:h,credentials:p})=>{let[g,f]=(0,x.useState)("chat"),[_,j]=(0,x.useState)(!1),[y,b]=(0,x.useState)(!1),[v,N]=(0,x.useState)(""),{accessToken:w,userRole:C,premiumUser:k,userId:S}=(0,r.default)(),{data:T,isLoading:I,error:F}=eB(),{data:P}=(0,tb.useGuardrails)(),M=P?.guardrails.map(e=>e.guardrail_name),{data:A,isLoading:E,error:L}=(0,tv.useTags)(),z=async()=>{b(!0),N(`test-${Date.now()}`),j(!0)},[q,V]=(0,x.useState)(!1),[D,H]=(0,x.useState)([]),[G,U]=(0,x.useState)(null);(0,x.useEffect)(()=>{(async()=>{H((await (0,l.modelAvailableCall)(w,"","",!1,null,!0,!0)).data.map(e=>e.id))})()},[w]);let $=(0,x.useMemo)(()=>T?[...T].sort((e,t)=>e.provider_display_name.localeCompare(t.provider_display_name)):[],[T]),K=F?F instanceof Error?F.message:"Failed to load providers":null,J=e0.all_admin_roles.includes(C),W=(0,e0.isUserTeamAdminForAnyTeam)(h,S);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(tG,{level:2,children:"Add Model"}),(0,t.jsx)(e_.Card,{children:(0,t.jsx)(el.Form,{form:e,onFinish:async e=>{await s().then(()=>{U(null)})},onFinishFailed:e=>{},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:(0,t.jsxs)(t.Fragment,{children:[W&&!J&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(el.Form.Item,{label:"Select Team",name:"team_id",rules:[{required:!0,message:"Please select a team to continue"}],tooltip:"Select the team for which you want to add this model",children:(0,t.jsx)(tC.default,{onChange:e=>{U(e)}})}),!G&&(0,t.jsx)(tw.Alert,{message:"Team Selection Required",description:"As a team admin, you need to select your team first before adding models.",type:"info",showIcon:!0,className:"mb-4"})]}),(J||W&&G)&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(el.Form.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc.",labelCol:{span:10},labelAlign:"left",children:(0,t.jsxs)(Y.Select,{virtual:!1,showSearch:!0,loading:I,placeholder:I?"Loading providers...":"Select a provider",optionFilterProp:"data-label",onChange:t=>{i(t),n(t),e.setFieldsValue({custom_llm_provider:t}),e.setFieldsValue({model:[],model_name:void 0})},children:[K&&0===$.length&&(0,t.jsx)(Y.Select.Option,{value:"",children:K},"__error"),$.map(e=>{let l=e.provider_display_name,s=e.provider;return eP.providerLogoMap[l],(0,t.jsx)(Y.Select.Option,{value:s,"data-label":l,children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(B.ProviderLogo,{provider:s,className:"w-5 h-5"}),(0,t.jsx)("span",{children:l})]})},s)})]})}),(0,t.jsx)(tD,{selectedProvider:a,providerModels:o,getPlaceholder:d}),(0,t.jsx)(tV,{}),(0,t.jsx)(el.Form.Item,{label:"Mode",name:"mode",className:"mb-1",children:(0,t.jsx)(Y.Select,{style:{width:"100%"},value:g,onChange:e=>f(e),options:tH})}),(0,t.jsxs)(eD.Row,{children:[(0,t.jsx)(eq.Col,{span:10}),(0,t.jsx)(eq.Col,{span:10,children:(0,t.jsxs)(em.Text,{className:"mb-5 mt-1",children:[(0,t.jsx)("strong",{children:"Optional"})," - LiteLLM endpoint to use when health checking this model"," ",(0,t.jsx)(tU,{href:"https://docs.litellm.ai/docs/proxy/health#health",target:"_blank",children:"Learn more"})]})})]}),(0,t.jsx)("div",{className:"mb-4",children:(0,t.jsx)(R.Typography.Text,{className:"text-sm text-gray-500 mb-2",children:"Either select existing credentials OR enter new provider credentials below"})}),(0,t.jsx)(el.Form.Item,{label:"Existing Credentials",name:"litellm_credential_name",initialValue:null,children:(0,t.jsx)(Y.Select,{showSearch:!0,placeholder:"Select or search for existing credentials",optionFilterProp:"children",filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:[{value:null,label:"None"},...p.map(e=>({value:e.credential_name,label:e.credential_name}))],allowClear:!0})}),(0,t.jsx)(el.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.litellm_credential_name!==t.litellm_credential_name||e.provider!==t.provider,children:({getFieldValue:e})=>e("litellm_credential_name")?null:(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex items-center my-4",children:[(0,t.jsx)("div",{className:"grow border-t border-gray-200"}),(0,t.jsx)("span",{className:"px-4 text-gray-500 text-sm",children:"OR"}),(0,t.jsx)("div",{className:"grow border-t border-gray-200"})]}),(0,t.jsx)(eK,{selectedProvider:a,uploadProps:c})]})}),(0,t.jsxs)("div",{className:"flex items-center my-4",children:[(0,t.jsx)("div",{className:"grow border-t border-gray-200"}),(0,t.jsx)("span",{className:"px-4 text-gray-500 text-sm",children:"Additional Model Info Settings"}),(0,t.jsx)("div",{className:"grow border-t border-gray-200"})]}),(J||!W)&&(0,t.jsx)(el.Form.Item,{label:"Team-BYOK Model",tooltip:"Only use this model + credential combination for this team. Useful when teams want to onboard their own OpenAI keys.",className:"mb-4",children:(0,t.jsx)(O.Tooltip,{title:k?"":"This is an enterprise-only feature. Upgrade to premium to restrict model+credential combinations to a specific team.",placement:"top",children:(0,t.jsx)(tN.Switch,{checked:q,onChange:t=>{V(t),t||e.setFieldValue("team_id",void 0)},disabled:!k})})}),q&&(J||!W)&&(0,t.jsx)(el.Form.Item,{label:"Select Team",name:"team_id",className:"mb-4",tooltip:"Only keys for this team will be able to call this model.",rules:[{required:q&&!J,message:"Please select a team."}],children:(0,t.jsx)(tC.default,{disabled:!k})}),J&&(0,t.jsx)(t.Fragment,{children:(0,t.jsx)(el.Form.Item,{label:"Model Access Group",name:"model_access_group",className:"mb-4",tooltip:"Use model access groups to give users access to select models, and add new ones to the group over time.",children:(0,t.jsx)(Y.Select,{mode:"tags",showSearch:!0,placeholder:"Select existing groups or type to create new ones",optionFilterProp:"children",tokenSeparators:[","],options:D.map(e=>({value:e,label:e})),maxTagCount:"responsive",allowClear:!0})})}),(0,t.jsx)(tR,{showAdvancedSettings:m,setShowAdvancedSettings:u,teams:h,guardrailsList:M||[],tagsList:A||{},accessToken:w||""})]}),(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(O.Tooltip,{title:"Get help on our github",children:(0,t.jsx)(R.Typography.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})}),(0,t.jsxs)("div",{className:"space-x-2",children:[(0,t.jsx)(Q.Button,{"data-testid":"test-connect-btn",onClick:z,loading:y,children:"Test Connect"}),(0,t.jsx)(Q.Button,{"data-testid":"add-model-btn",htmlType:"submit",children:"Add Model"})]})]})]})})}),(0,t.jsx)(es.Modal,{title:"Connection Test Results",open:_,onCancel:()=>{j(!1),b(!1)},footer:[(0,t.jsx)(Q.Button,{onClick:()=>{j(!1),b(!1)},children:"Close"},"close")],width:700,children:_&&(0,t.jsx)(ts,{formValues:e.getFieldsValue(),accessToken:w,testMode:g,modelName:e.getFieldValue("model_name")||e.getFieldValue("model"),onClose:()=>{j(!1),b(!1)},onTestComplete:()=>b(!1)},v)})]})},tK=({form:e,handleOk:l,selectedProvider:s,setSelectedProvider:a,providerModels:r,setProviderModelsFn:i,getPlaceholder:o,uploadProps:n,showAdvancedSettings:d,setShowAdvancedSettings:c,teams:m,credentials:u,accessToken:h,userRole:p})=>{let[x]=el.Form.useForm();return(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)(e6.TabGroup,{className:"w-full",children:[(0,t.jsxs)(e3.TabList,{className:"mb-4",children:[(0,t.jsx)(e5.Tab,{children:"Add Model"}),(0,t.jsx)(e5.Tab,{children:"Add Auto Router"})]}),(0,t.jsxs)(e8.TabPanels,{children:[(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(t$,{form:e,handleOk:l,selectedProvider:s,setSelectedProvider:a,providerModels:r,setProviderModelsFn:i,getPlaceholder:o,uploadProps:n,showAdvancedSettings:d,setShowAdvancedSettings:c,teams:m,credentials:u})}),(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(ty,{form:x,handleOk:()=>{x.validateFields().then(e=>{ta(e,h,x,l)}).catch(e=>{console.error("Validation failed:",e)})},accessToken:h,userRole:p})})]})]})})};var tJ=e.i(798496),tW=e.i(536916),tQ=e.i(502275),tY=e.i(122577);let tX=[{pattern:/Missing .* API Key/i,replacement:"Missing API Key"},{pattern:/Connection timeout/i,replacement:"Connection timeout"},{pattern:/Network.*not.*ok/i,replacement:"Network connection failed"},{pattern:/403.*Forbidden/i,replacement:"Access forbidden - check API key permissions"},{pattern:/401.*Unauthorized/i,replacement:"Unauthorized - invalid API key"},{pattern:/429.*rate limit/i,replacement:"Rate limit exceeded"},{pattern:/500.*Internal Server Error/i,replacement:"Provider internal server error"},{pattern:/litellm\.AuthenticationError/i,replacement:"Authentication failed"},{pattern:/litellm\.RateLimitError/i,replacement:"Rate limit exceeded"},{pattern:/litellm\.APIError/i,replacement:"API error"}],tZ=({accessToken:e,modelData:s,all_models_on_proxy:a,getDisplayModelName:r,setSelectedModelId:i,teams:o,isLoading:n=!1,paginationMeta:d,currentPage:c=1,pageSize:m=50,onPageChange:u})=>{let h,p,g,f,[_,j]=(0,x.useState)({}),[y,b]=(0,x.useState)([]),[v,N]=(0,x.useState)(!1),[w,C]=(0,x.useState)(!1),[k,S]=(0,x.useState)(null),[F,P]=(0,x.useState)(!1),[M,A]=(0,x.useState)(null);(0,x.useRef)(null),(0,x.useEffect)(()=>{e&&s?.data&&(async()=>{let t={};s.data.forEach(e=>{let l=e.model_info?.id;l&&(t[l]={status:"none",lastCheck:"None",lastSuccess:"None",loading:!1,error:void 0,fullError:void 0,successResponse:void 0})});try{let a=await (0,l.latestHealthChecksCall)(e);a&&a.latest_health_checks&&"object"==typeof a.latest_health_checks&&Object.entries(a.latest_health_checks).forEach(([e,l])=>{if(!l||!s.data.some(t=>t.model_info?.id===e))return;let a=l.error_message||void 0;t[e]={status:l.status||"unknown",lastCheck:l.checked_at?new Date(l.checked_at).toLocaleString():"None",lastSuccess:"healthy"===l.status&&l.checked_at?new Date(l.checked_at).toLocaleString():"None",loading:!1,error:a?E(a):void 0,fullError:a,successResponse:"healthy"===l.status?l:void 0}})}catch(e){console.warn("Failed to load health check history (using default states):",e)}j(t)})()},[e,s]);let E=e=>{if(!e)return"Health check failed";let t="string"==typeof e?e:JSON.stringify(e),l=t.match(/(\w+Error):\s*(\d{3})/i);if(l)return`${l[1]}: ${l[2]}`;let s=t.match(/(AuthenticationError|RateLimitError|BadRequestError|InternalServerError|TimeoutError|NotFoundError|ForbiddenError|ServiceUnavailableError|BadGatewayError|ContentPolicyViolationError|\w+Error)/i),a=t.match(/\b(400|401|403|404|408|429|500|502|503|504)\b/);if(s&&a)return`${s[1]}: ${a[1]}`;if(a){let e=a[1];return`${({400:"BadRequestError",401:"AuthenticationError",403:"ForbiddenError",404:"NotFoundError",408:"TimeoutError",429:"RateLimitError",500:"InternalServerError",502:"BadGatewayError",503:"ServiceUnavailableError",504:"GatewayTimeoutError"})[e]}: ${e}`}if(s){let e=s[1],t={AuthenticationError:"401",RateLimitError:"429",BadRequestError:"400",InternalServerError:"500",TimeoutError:"408",NotFoundError:"404",ForbiddenError:"403",ServiceUnavailableError:"503",BadGatewayError:"502",GatewayTimeoutError:"504",ContentPolicyViolationError:"400"}[e];return t?`${e}: ${t}`:e}for(let{pattern:e,replacement:l}of tX)if(e.test(t))return l;if(/missing.*api.*key|invalid.*key|unauthorized/i.test(t))return"AuthenticationError: 401";if(/rate.*limit|too.*many.*requests/i.test(t))return"RateLimitError: 429";if(/timeout|timed.*out/i.test(t))return"TimeoutError: 408";if(/not.*found/i.test(t))return"NotFoundError: 404";if(/forbidden|access.*denied/i.test(t))return"ForbiddenError: 403";if(/internal.*server.*error/i.test(t))return"InternalServerError: 500";let r=t.replace(/[\n\r]+/g," ").replace(/\s+/g," ").trim(),i=r.split(/[.!?]/),o=i[0]?.trim();return o&&o.length>0?o.length>100?o.substring(0,97)+"...":o:r.length>100?r.substring(0,97)+"...":r},L=async t=>{if(e){j(e=>({...e,[t]:{...e[t],loading:!0,status:"checking"}}));try{let s=await (0,l.individualModelHealthCheckCall)(e,t),a=new Date().toLocaleString();if(s.unhealthy_count>0&&s.unhealthy_endpoints&&s.unhealthy_endpoints.length>0){let e=s.unhealthy_endpoints[0]?.error||"Health check failed",l=E(e);j(s=>({...s,[t]:{status:"unhealthy",lastCheck:a,lastSuccess:s[t]?.lastSuccess||"None",loading:!1,error:l,fullError:e}}))}else j(e=>({...e,[t]:{status:"healthy",lastCheck:a,lastSuccess:a,loading:!1,successResponse:s}}));try{let s=await (0,l.latestHealthChecksCall)(e),a=s.latest_health_checks?.[t];if(a){let e=a.error_message||void 0;j(l=>({...l,[t]:{status:a.status||l[t]?.status||"unknown",lastCheck:a.checked_at?new Date(a.checked_at).toLocaleString():l[t]?.lastCheck||"None",lastSuccess:"healthy"===a.status&&a.checked_at?new Date(a.checked_at).toLocaleString():l[t]?.lastSuccess||"None",loading:!1,error:e?E(e):l[t]?.error,fullError:e||l[t]?.fullError,successResponse:"healthy"===a.status?a:l[t]?.successResponse}}))}}catch(e){}}catch(a){let e=new Date().toLocaleString(),l=a instanceof Error?a.message:String(a),s=E(l);j(a=>({...a,[t]:{status:"unhealthy",lastCheck:e,lastSuccess:a[t]?.lastSuccess||"None",loading:!1,error:s,fullError:l}}))}}},R=async()=>{let t=y.length>0?y:a,s=t.reduce((e,t)=>(e[t]={..._[t],loading:!0,status:"checking"},e),{});j(e=>({...e,...s}));let r={},i=t.map(async t=>{if(e)try{let s=await (0,l.individualModelHealthCheckCall)(e,t);r[t]=s;let a=new Date().toLocaleString();if(s.unhealthy_count>0&&s.unhealthy_endpoints&&s.unhealthy_endpoints.length>0){let e=s.unhealthy_endpoints[0]?.error||"Health check failed",l=E(e);j(s=>({...s,[t]:{status:"unhealthy",lastCheck:a,lastSuccess:s[t]?.lastSuccess||"None",loading:!1,error:l,fullError:e}}))}else j(e=>({...e,[t]:{status:"healthy",lastCheck:a,lastSuccess:a,loading:!1,successResponse:s}}))}catch(a){console.error(`Health check failed for model id ${t}:`,a);let e=new Date().toLocaleString(),l=a instanceof Error?a.message:String(a),s=E(l);j(a=>({...a,[t]:{status:"unhealthy",lastCheck:e,lastSuccess:a[t]?.lastSuccess||"None",loading:!1,error:s,fullError:l}}))}});await Promise.allSettled(i);try{if(!e)return;let s=await (0,l.latestHealthChecksCall)(e);s.latest_health_checks&&Object.entries(s.latest_health_checks).forEach(([e,l])=>{if(t.includes(e)&&l){let t=l.error_message||void 0;j(s=>{let a=s[e];return{...s,[e]:{status:l.status||a?.status||"unknown",lastCheck:l.checked_at?new Date(l.checked_at).toLocaleString():a?.lastCheck||"None",lastSuccess:"healthy"===l.status&&l.checked_at?new Date(l.checked_at).toLocaleString():a?.lastSuccess||"None",loading:!1,error:t?E(t):a?.error,fullError:t||a?.fullError,successResponse:"healthy"===l.status?l:a?.successResponse}}})}})}catch(e){console.warn("Failed to fetch updated health statuses from database (non-critical):",e)}},B=e=>{N(e),e?b(a):b([])},z=e=>{b([]),N(!1),j({}),u?.(e)},q=()=>{C(!1),S(null)},V=()=>{P(!1),A(null)},D=(s?.data??[]).map(e=>{let t=e.model_info?.id,l=(t?_[t]:null)||{status:"none",lastCheck:"None",loading:!1};return{model_name:e.model_name,model_info:e.model_info,provider:e.provider,litellm_model_name:e.litellm_model_name,health_status:l.status,last_check:l.lastCheck,last_success:l.lastSuccess||"None",health_loading:l.loading,health_error:l.error,health_full_error:l.fullError}}),H=!!(d&&u),G=d?.total_count??0,U=d?.total_pages??1,$=d?.current_page??c,K=d?.size??m,J=H&&G>0?($-1)*K+1:0,W=H?Math.min($*K,G):0;return(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(eu.Title,{children:"Model Health Status"}),(0,t.jsx)(em.Text,{className:"text-gray-600 mt-1",children:"Run health checks on individual models to verify they are working correctly"})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[y.length>0&&(0,t.jsx)(I.Button,{size:"sm",variant:"light",onClick:()=>B(!1),className:"px-3 py-1 text-sm",children:"Clear Selection"}),(0,t.jsx)(I.Button,{size:"sm",variant:"secondary",onClick:R,disabled:Object.values(_).some(e=>e.loading),className:"px-3 py-1 text-sm",children:y.length>0&&y.length0?`Showing ${J} - ${W} of ${G} results`:"Showing 0 results"}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("button",{onClick:()=>z(c-1),disabled:n||1===c,className:`px-3 py-1 text-sm border rounded-md ${n||1===c?"bg-gray-100 text-gray-400 cursor-not-allowed":"hover:bg-gray-50"}`,children:"Previous"}),(0,t.jsx)("button",{onClick:()=>z(c+1),disabled:n||c>=U,className:`px-3 py-1 text-sm border rounded-md ${n||c>=U?"bg-gray-100 text-gray-400 cursor-not-allowed":"hover:bg-gray-50"}`,children:"Next"})]})]}),(0,t.jsx)(tJ.ModelDataTable,{columns:(h=(e,t)=>{t?b(t=>[...t,e]):(b(t=>t.filter(t=>t!==e)),N(!1))},p=e=>{switch(e){case"healthy":return(0,t.jsx)(T.Badge,{color:"emerald",children:"healthy"});case"unhealthy":return(0,t.jsx)(T.Badge,{color:"red",children:"unhealthy"});case"checking":return(0,t.jsx)(T.Badge,{color:"blue",children:"checking"});case"none":return(0,t.jsx)(T.Badge,{color:"gray",children:"none"});default:return(0,t.jsx)(T.Badge,{color:"gray",children:"unknown"})}},g=(e,t,l)=>{S({modelName:e,cleanedError:t,fullError:l}),C(!0)},f=(e,t)=>{A({modelName:e,response:t}),P(!0)},[{header:()=>(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(tW.Checkbox,{checked:v,indeterminate:y.length>0&&!v,onChange:e=>B(e.target.checked),onClick:e=>e.stopPropagation()}),(0,t.jsx)("span",{children:"Model ID"})]}),accessorKey:"model_info.id",enableSorting:!0,sortingFn:"alphanumeric",cell:({row:e})=>{let l=e.original,s=l.model_info?.id??"",a=y.includes(s);return(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(tW.Checkbox,{checked:a,onChange:e=>h(s,e.target.checked),onClick:e=>e.stopPropagation()}),(0,t.jsx)(O.Tooltip,{title:l.model_info.id,children:(0,t.jsx)("div",{className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left w-full truncate whitespace-nowrap cursor-pointer max-w-[15ch]",onClick:()=>i&&i(l.model_info.id),children:l.model_info.id})})]})}},{header:"Model Name",accessorKey:"model_name",enableSorting:!0,sortingFn:"alphanumeric",cell:({row:e})=>{let l=e.original,s=r(l)||l.model_name;return(0,t.jsx)("div",{className:"font-medium text-sm",children:(0,t.jsx)(O.Tooltip,{title:s,children:(0,t.jsx)("div",{className:"truncate max-w-[200px]",children:s})})})}},{header:"Team Alias",accessorKey:"model_info.team_id",enableSorting:!0,sortingFn:"alphanumeric",cell:({row:e})=>{let l=e.original,s=l.model_info?.team_id;if(!s)return(0,t.jsx)("span",{className:"text-gray-400 text-sm",children:"-"});let a=o?.find(e=>e.team_id===s),r=a?.team_alias||s;return(0,t.jsx)("div",{className:"text-sm",children:(0,t.jsx)(O.Tooltip,{title:r,children:(0,t.jsx)("div",{className:"truncate max-w-[150px]",children:r})})})}},{header:"Health Status",accessorKey:"health_status",enableSorting:!0,sortingFn:(e,t,l)=>{let s=e.getValue("health_status")||"unknown",a=t.getValue("health_status")||"unknown",r={healthy:0,checking:1,unknown:2,unhealthy:3};return(r[s]??4)-(r[a]??4)},cell:({row:e})=>{let l=e.original,s={status:l.health_status,loading:l.health_loading,error:l.health_error};if(s.loading)return(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsxs)("div",{className:"flex space-x-1",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-indigo-500 rounded-full animate-pulse"}),(0,t.jsx)("div",{className:"w-2 h-2 bg-indigo-500 rounded-full animate-pulse",style:{animationDelay:"0.2s"}}),(0,t.jsx)("div",{className:"w-2 h-2 bg-indigo-500 rounded-full animate-pulse",style:{animationDelay:"0.4s"}})]}),(0,t.jsx)(em.Text,{className:"text-gray-600 text-sm",children:"Checking..."})]});let a=l.model_info?.id??"",i=r(l)||l.model_name,o="healthy"===s.status&&_[a]?.successResponse;return(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[p(s.status),o&&f&&(0,t.jsx)(O.Tooltip,{title:"View response details",placement:"top",children:(0,t.jsx)("button",{onClick:()=>f(i,_[a]?.successResponse),className:"p-1 text-green-600 hover:text-green-800 hover:bg-green-50 rounded-sm cursor-pointer transition-colors",children:(0,t.jsx)(tQ.InformationCircleIcon,{className:"h-4 w-4"})})})]})}},{header:"Error Details",accessorKey:"health_error",enableSorting:!1,cell:({row:e})=>{let l=e.original,s=l.model_info?.id??"",a=r(l)||l.model_name,i=_[s];if(!i?.error)return(0,t.jsx)(em.Text,{className:"text-gray-400 text-sm",children:"No errors"});let o=i.error,n=i.fullError||i.error;return(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"max-w-[200px]",children:(0,t.jsx)(O.Tooltip,{title:o,placement:"top",children:(0,t.jsx)(em.Text,{className:"text-red-600 text-sm truncate",children:o})})}),g&&n!==o&&(0,t.jsx)(O.Tooltip,{title:"View full error details",placement:"top",children:(0,t.jsx)("button",{onClick:()=>g(a,o,n),className:"p-1 text-red-600 hover:text-red-800 hover:bg-red-50 rounded-sm cursor-pointer transition-colors",children:(0,t.jsx)(tQ.InformationCircleIcon,{className:"h-4 w-4"})})})]})}},{header:"Last Check",accessorKey:"last_check",enableSorting:!0,sortingFn:(e,t,l)=>{let s=e.getValue("last_check")||"Never checked",a=t.getValue("last_check")||"Never checked";if("Never checked"===s&&"Never checked"===a)return 0;if("Never checked"===s)return 1;if("Never checked"===a)return -1;if("Check in progress..."===s&&"Check in progress..."===a)return 0;if("Check in progress..."===s)return -1;if("Check in progress..."===a)return 1;let r=new Date(s),i=new Date(a);return isNaN(r.getTime())&&isNaN(i.getTime())?0:isNaN(r.getTime())?1:isNaN(i.getTime())?-1:i.getTime()-r.getTime()},cell:({row:e})=>{let l=e.original;return(0,t.jsx)(em.Text,{className:"text-gray-600 text-sm",children:l.health_loading?"Check in progress...":l.last_check})}},{header:"Last Success",accessorKey:"last_success",enableSorting:!0,sortingFn:(e,t,l)=>{let s=e.getValue("last_success")||"Never succeeded",a=t.getValue("last_success")||"Never succeeded";if("Never succeeded"===s&&"Never succeeded"===a)return 0;if("Never succeeded"===s)return 1;if("Never succeeded"===a)return -1;if("None"===s&&"None"===a)return 0;if("None"===s)return 1;if("None"===a)return -1;let r=new Date(s),i=new Date(a);return isNaN(r.getTime())&&isNaN(i.getTime())?0:isNaN(r.getTime())?1:isNaN(i.getTime())?-1:i.getTime()-r.getTime()},cell:({row:e})=>{let l=e.original,s=_[l.model_info?.id??""],a=s?.lastSuccess||"None";return(0,t.jsx)(em.Text,{className:"text-gray-600 text-sm",children:a})}},{header:"Actions",id:"actions",cell:({row:e})=>{let l=e.original,s=l.model_info?.id??"",a=l.health_status&&"none"!==l.health_status,r=l.health_loading?"Checking...":a?"Re-run Health Check":"Run Health Check";return(0,t.jsx)(O.Tooltip,{title:r,placement:"top",children:(0,t.jsx)("button",{"data-testid":"run-health-check-btn",className:`p-2 rounded-md transition-colors ${l.health_loading?"text-gray-400 cursor-not-allowed bg-gray-100":"text-indigo-600 hover:text-indigo-700 hover:bg-indigo-50"}`,onClick:()=>{l.health_loading||L(s)},disabled:l.health_loading,children:l.health_loading?(0,t.jsxs)("div",{className:"flex space-x-1",children:[(0,t.jsx)("div",{className:"w-1 h-1 bg-gray-400 rounded-full animate-pulse"}),(0,t.jsx)("div",{className:"w-1 h-1 bg-gray-400 rounded-full animate-pulse",style:{animationDelay:"0.2s"}}),(0,t.jsx)("div",{className:"w-1 h-1 bg-gray-400 rounded-full animate-pulse",style:{animationDelay:"0.4s"}})]}):a?(0,t.jsx)(e2.RefreshIcon,{className:"h-4 w-4"}):(0,t.jsx)(tY.PlayIcon,{className:"h-4 w-4"})})})},enableSorting:!1}]),data:D,isLoading:n})]}),(0,t.jsx)(es.Modal,{title:k?`Health Check Error - ${k.modelName}`:"Error Details",open:w,onCancel:q,footer:[(0,t.jsx)(Q.Button,{onClick:q,children:"Close"},"close")],width:800,children:k&&(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Error:"}),(0,t.jsx)("div",{className:"mt-2 p-3 bg-red-50 border border-red-200 rounded-md",children:(0,t.jsx)(em.Text,{className:"text-red-800",children:k.cleanedError})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Full Error Details:"}),(0,t.jsx)("div",{className:"mt-2 p-3 bg-gray-50 border border-gray-200 rounded-md max-h-96 overflow-y-auto",children:(0,t.jsx)("pre",{className:"text-sm text-gray-800 whitespace-pre-wrap",children:k.fullError})})]})]})}),(0,t.jsx)(es.Modal,{title:M?`Health Check Response - ${M.modelName}`:"Response Details",open:F,onCancel:V,footer:[(0,t.jsx)(Q.Button,{onClick:V,children:"Close"},"close")],width:800,children:M&&(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Status:"}),(0,t.jsx)("div",{className:"mt-2 p-3 bg-green-50 border border-green-200 rounded-md",children:(0,t.jsx)(em.Text,{className:"text-green-800",children:"Health check passed successfully"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Response Details:"}),(0,t.jsx)("div",{className:"mt-2 p-3 bg-gray-50 border border-gray-200 rounded-md max-h-96 overflow-y-auto",children:(0,t.jsx)("pre",{className:"text-sm text-gray-800 whitespace-pre-wrap",children:JSON.stringify(M.response,null,2)})})]})]})})]})};var t0=e.i(250980),t1=e.i(797672),t2=e.i(871943),t4=e.i(502547);let t5=({accessToken:e,initialModelGroupAlias:s={},onAliasUpdate:a})=>{let[r,i]=(0,x.useState)([]),[o,n]=(0,x.useState)({aliasName:"",targetModelGroup:""}),[d,c]=(0,x.useState)(null),[m,u]=(0,x.useState)(!0);(0,x.useEffect)(()=>{i(Object.entries(s).map(([e,t],l)=>({id:`${l}-${e}`,aliasName:e,targetModelGroup:"string"==typeof t?t:t?.model??""})))},[s]);let h=async t=>{if(!e)return console.error("Access token is missing"),!1;try{let s={};return t.forEach(e=>{s[e.aliasName]=e.targetModelGroup}),await (0,l.setCallbacksCall)(e,{router_settings:{model_group_alias:s}}),a&&a(s),!0}catch(e){return console.error("Failed to save model group alias settings:",e),G.default.fromBackend("Failed to save model group alias settings"),!1}},p=async()=>{if(!o.aliasName||!o.targetModelGroup)return void G.default.fromBackend("Please provide both alias name and target model group");if(r.some(e=>e.aliasName===o.aliasName))return void G.default.fromBackend("An alias with this name already exists");let e=[...r,{id:`${Date.now()}-${o.aliasName}`,aliasName:o.aliasName,targetModelGroup:o.targetModelGroup}];await h(e)&&(i(e),n({aliasName:"",targetModelGroup:""}),G.default.success("Alias added successfully"))},v=async()=>{if(!d)return;if(!d.aliasName||!d.targetModelGroup)return void G.default.fromBackend("Please provide both alias name and target model group");if(r.some(e=>e.id!==d.id&&e.aliasName===d.aliasName))return void G.default.fromBackend("An alias with this name already exists");let e=r.map(e=>e.id===d.id?d:e);await h(e)&&(i(e),c(null),G.default.success("Alias updated successfully"))},N=()=>{c(null)},w=async e=>{let t=r.filter(t=>t.id!==e);await h(t)&&(i(t),G.default.success("Alias deleted successfully"))},C=r.reduce((e,t)=>(e[t.aliasName]=t.targetModelGroup,e),{});return(0,t.jsxs)(eL.Card,{className:"mb-6",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between cursor-pointer",onClick:()=>u(!m),children:[(0,t.jsxs)("div",{className:"flex flex-col",children:[(0,t.jsx)(eu.Title,{className:"mb-0",children:"Model Group Alias Settings"}),(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"Create aliases for your model groups to simplify API calls. For example, you can create an alias 'gpt-4o' that points to 'gpt-4o-mini-openai' model group."})]}),(0,t.jsx)("div",{className:"flex items-center",children:m?(0,t.jsx)(t2.ChevronDownIcon,{className:"w-5 h-5 text-gray-500"}):(0,t.jsx)(t4.ChevronRightIcon,{className:"w-5 h-5 text-gray-500"})})]}),m&&(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(em.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:o.aliasName,onChange:e=>n({...o,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model Group"}),(0,t.jsx)("input",{type:"text",value:o.targetModelGroup,onChange:e=>n({...o,targetModelGroup:e.target.value}),placeholder:"e.g., gpt-4o-mini-openai",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:p,disabled:!o.aliasName||!o.targetModelGroup,className:`flex items-center px-4 py-2 rounded-md text-sm ${!o.aliasName||!o.targetModelGroup?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(t0.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(em.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(g.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(f.TableHead,{children:(0,t.jsxs)(y.TableRow,{children:[(0,t.jsx)(_.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(_.TableHeaderCell,{className:"py-1 h-8",children:"Target Model Group"}),(0,t.jsx)(_.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(j.TableBody,{children:[r.map(e=>(0,t.jsx)(y.TableRow,{className:"h-8",children:d&&d.id===e.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:d.aliasName,onChange:e=>c({...d,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(b.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:d.targetModelGroup,onChange:e=>c({...d,targetModelGroup:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(b.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:v,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:N,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.TableCell,{className:"py-0.5 text-sm text-gray-900",children:e.aliasName}),(0,t.jsx)(b.TableCell,{className:"py-0.5 text-sm text-gray-500",children:e.targetModelGroup}),(0,t.jsx)(b.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{c({...e})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(t1.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>w(e.id),className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(S.TrashIcon,{className:"w-3 h-3"})})]})})]})},e.id)),0===r.length&&(0,t.jsx)(y.TableRow,{children:(0,t.jsx)(b.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(eu.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(em.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config.yaml:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["router_settings:",(0,t.jsx)("br",{}),"  model_group_alias:",0===Object.keys(C).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"    # No aliases configured yet"]}):Object.entries(C).map(([e,l])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'    "',e,'": "',l,'"']},e))]})})]})]})]})};var t6=e.i(530212);let t3=x.forwardRef(function(e,t){return x.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),x.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"}))});var t8=e.i(678784),t7=e.i(118366),t9=e.i(500330);let le=({isVisible:e,onCancel:s,onSuccess:a,modelData:r,accessToken:i,userRole:o})=>{let[n]=el.Form.useForm(),[d,c]=(0,x.useState)(!1),[m,u]=(0,x.useState)([]),[h,p]=(0,x.useState)([]),[g,f]=(0,x.useState)(!1),[_,j]=(0,x.useState)(!1),[y,b]=(0,x.useState)(null);(0,x.useEffect)(()=>{e&&r&&v()},[e,r]),(0,x.useEffect)(()=>{let t=async()=>{if(i)try{let e=await (0,l.modelAvailableCall)(i,"","",!1,null,!0,!0);u(e.data.map(e=>e.id))}catch(e){console.error("Error fetching model access groups:",e)}},s=async()=>{if(i)try{let e=await (0,tr.fetchAvailableModels)(i);p(e)}catch(e){console.error("Error fetching model info:",e)}};e&&(t(),s())},[e,i]);let v=()=>{try{let e=null;r.litellm_params?.auto_router_config&&(e="string"==typeof r.litellm_params.auto_router_config?JSON.parse(r.litellm_params.auto_router_config):r.litellm_params.auto_router_config),b(e),n.setFieldsValue({auto_router_name:r.model_name,auto_router_default_model:r.litellm_params?.auto_router_default_model||"",auto_router_embedding_model:r.litellm_params?.auto_router_embedding_model||"",model_access_group:r.model_info?.access_groups||[]});let t=new Set(h.map(e=>e.model_group));f(!t.has(r.litellm_params?.auto_router_default_model)),j(!t.has(r.litellm_params?.auto_router_embedding_model))}catch(e){console.error("Error parsing auto router config:",e),G.default.fromBackend("Error loading auto router configuration")}},N=async()=>{try{c(!0);let e=await n.validateFields(),t={...r.litellm_params,auto_router_config:JSON.stringify(y),auto_router_default_model:e.auto_router_default_model,auto_router_embedding_model:e.auto_router_embedding_model||void 0},o={...r.model_info,access_groups:e.model_access_group||[]},d={model_name:e.auto_router_name,litellm_params:t,model_info:o};await (0,l.modelPatchUpdateCall)(i,d,r.model_info.id);let m={...r,model_name:e.auto_router_name,litellm_params:t,model_info:o};G.default.success("Auto router configuration updated successfully"),a(m),s()}catch(e){console.error("Error updating auto router:",e),G.default.fromBackend("Failed to update auto router configuration")}finally{c(!1)}},w=h.map(e=>({value:e.model_group,label:e.model_group}));return(0,t.jsx)(es.Modal,{title:"Edit Auto Router Configuration",open:e,onCancel:s,footer:[(0,t.jsx)(Q.Button,{onClick:s,children:"Cancel"},"cancel"),(0,t.jsx)(Q.Button,{loading:d,onClick:N,children:"Save Changes"},"submit")],width:1e3,destroyOnHidden:!0,children:(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)(em.Text,{className:"text-gray-600",children:"Edit the auto router configuration including routing logic, default models, and access settings."}),(0,t.jsxs)(el.Form,{form:n,layout:"vertical",className:"space-y-4",children:[(0,t.jsx)(el.Form.Item,{label:"Auto Router Name",name:"auto_router_name",rules:[{required:!0,message:"Auto router name is required"}],children:(0,t.jsx)(eO.TextInput,{placeholder:"e.g., auto_router_1, smart_routing"})}),(0,t.jsx)("div",{className:"w-full",children:(0,t.jsx)(tu,{modelInfo:h,value:y,onChange:e=>{b(e)}})}),(0,t.jsx)(el.Form.Item,{label:"Default Model",name:"auto_router_default_model",rules:[{required:!0,message:"Default model is required"}],children:(0,t.jsx)(Y.Select,{placeholder:"Select a default model",onChange:e=>{f("custom"===e)},options:[...w,{value:"custom",label:"Enter custom model name"}],showSearch:!0})}),(0,t.jsx)(el.Form.Item,{label:"Embedding Model",name:"auto_router_embedding_model",children:(0,t.jsx)(Y.Select,{placeholder:"Select an embedding model (optional)",onChange:e=>{j("custom"===e)},options:[...w,{value:"custom",label:"Enter custom model name"}],showSearch:!0,allowClear:!0})}),"Admin"===o&&(0,t.jsx)(el.Form.Item,{label:"Model Access Groups",name:"model_access_group",tooltip:"Control who can access this auto router",children:(0,t.jsx)(Y.Select,{mode:"tags",showSearch:!0,placeholder:"Select existing groups or type to create new ones",optionFilterProp:"children",tokenSeparators:[","],options:m.map(e=>({value:e,label:e})),maxTagCount:"responsive",allowClear:!0})})]})]})})},{Title:lt,Link:ll}=R.Typography,ls=({isVisible:e,onCancel:l,onAddCredential:s,existingCredential:a,setIsCredentialModalOpen:r})=>{let[i]=el.Form.useForm();return(0,t.jsx)(es.Modal,{title:"Reuse Credentials",open:e,onCancel:()=>{l(),i.resetFields()},footer:null,width:600,children:(0,t.jsxs)(el.Form,{form:i,onFinish:e=>{s(e),i.resetFields(),r(!1)},layout:"vertical",children:[(0,t.jsx)(el.Form.Item,{label:"Credential Name:",name:"credential_name",rules:[{required:!0,message:"Credential name is required"}],initialValue:a?.credential_name,children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter a friendly name for these credentials"})}),Object.entries(a?.credential_values||{}).map(([e,l])=>(0,t.jsx)(el.Form.Item,{label:e,name:e,initialValue:l,children:(0,t.jsx)(eO.TextInput,{placeholder:`Enter ${e}`,disabled:!0})},e)),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)(O.Tooltip,{title:"Get help on our github",children:(0,t.jsx)(ll,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})}),(0,t.jsxs)("div",{children:[(0,t.jsx)(Q.Button,{onClick:()=>{l(),i.resetFields()},style:{marginRight:10},children:"Cancel"}),(0,t.jsx)(Q.Button,{htmlType:"submit",children:"Reuse Credentials"})]})]})]})})},{Text:la}=R.Typography;function lr({open:e,onCancel:s,accessToken:a,modelId:r,onUpdated:i}){let[o]=el.Form.useForm(),[n,d]=(0,x.useState)(!1),c=()=>{o.resetFields(),s()},m=async e=>{let t=e.api_key?.trim();if(!t)return void G.default.fromBackend("Enter a new API key");d(!0);try{await (0,l.modelPatchUpdateCall)(a,{litellm_params:{api_key:t},model_info:{id:r}},r),G.default.success("API key updated"),o.resetFields(),i(),s()}catch(e){console.error("Error updating API key:",e),G.default.fromBackend("Failed to update API key")}finally{d(!1)}};return(0,t.jsxs)(es.Modal,{title:"Update API Key",open:e,onCancel:c,footer:null,width:520,destroyOnHidden:!0,children:[(0,t.jsx)(la,{className:"block mb-4 text-gray-500",children:"Update this model's API key. Only the new key is sent; the rest of the deployment configuration is left untouched."}),(0,t.jsx)(tw.Alert,{type:"warning",showIcon:!0,className:"mb-4",message:"Only the API key is rotated here. Models that authenticate with an Azure AD token, AWS credentials, or a Vertex service-account JSON aren't supported yet; update those from the model's LiteLLM Params for now."}),(0,t.jsxs)(el.Form,{form:o,onFinish:m,layout:"vertical",children:[(0,t.jsx)(el.Form.Item,{label:"New API Key",name:"api_key",rules:[{required:!0,message:"Enter a new API key"}],children:(0,t.jsx)(eV.Input.Password,{placeholder:"Enter the new API key",autoComplete:"new-password"})}),(0,t.jsxs)("div",{className:"flex justify-end items-center mt-4",children:[(0,t.jsx)(Q.Button,{onClick:c,style:{marginRight:10},children:"Cancel"}),(0,t.jsx)(Q.Button,{type:"primary",htmlType:"submit",loading:n,children:"Update API Key"})]})]})]})}let li=e=>"string"==typeof e&&/\*{2,}/.test(e);function lo({modelId:e,onClose:s,accessToken:a,userID:r,userRole:i,onModelUpdate:o,modelAccessGroups:c}){let m,[u]=el.Form.useForm(),h=(0,$.useQueryClient)(),[p,g]=(0,x.useState)(null),[f,_]=(0,x.useState)(!1),[j,y]=(0,x.useState)(!1),[b,v]=(0,x.useState)(!1),[N,w]=(0,x.useState)(!1),[k,T]=(0,x.useState)(!1),[F,P]=(0,x.useState)(!1),[M,A]=(0,x.useState)(!1),[E,L]=(0,x.useState)(null),[R,B]=(0,x.useState)(!1),[z,q]=(0,x.useState)({}),[V,U]=(0,x.useState)(!1),[W,X]=(0,x.useState)([]),[Z,ee]=(0,x.useState)({}),[et,ea]=(0,x.useState)([]),{data:er,isLoading:eo}=(0,d.useModelsInfo)(1,50,void 0,e),{data:en}=(0,n.useModelCostMap)(),{data:ed}=(0,d.useModelHub)(),ec=e=>null!=en&&"object"==typeof en&&e in en?en[e].litellm_provider:"openai",eh=(0,x.useMemo)(()=>er?.data&&0!==er.data.length&&ei(er,ec).data[0]||null,[er,en]),ep=("Admin"===i||eh?.model_info?.created_by===r)&&eh?.model_info?.db_model,ex="Admin"===i,eg=eh?.litellm_params?.auto_router_config!=null,ef=eh?.litellm_params?.litellm_credential_name!=null&&eh?.litellm_params?.litellm_credential_name!=void 0;(0,x.useEffect)(()=>{if(eh&&!p){let e=eh;e.litellm_model_name||(e={...e,litellm_model_name:e?.litellm_params?.litellm_model_name??e?.litellm_params?.model??e?.model_info?.key??null}),g(e),e?.litellm_params?.cache_control_injection_points&&B(!0)}},[eh,p]),(0,x.useEffect)(()=>{let t=async()=>{if(!a||eh)return;let t=(await (0,l.modelInfoV1Call)(a,e)).data[0];t&&!t.litellm_model_name&&(t={...t,litellm_model_name:t?.litellm_params?.litellm_model_name??t?.litellm_params?.model??t?.model_info?.key??null}),g(t),t?.litellm_params?.cache_control_injection_points&&B(!0)},s=async()=>{if(a)try{let e=(await (0,l.getGuardrailsList)(a)).guardrails.map(e=>e.guardrail_name);X(e)}catch(e){console.error("Failed to fetch guardrails:",e)}},r=async()=>{if(a)try{let e=await (0,l.tagListCall)(a);ee(e)}catch(e){console.error("Failed to fetch tags:",e)}},i=async()=>{if(a)try{let e=await (0,l.credentialListCall)(a);ea(e.credentials||[])}catch(e){console.error("Failed to fetch credentials:",e)}};(async()=>{if(!a||ef)return;let t=await (0,l.credentialGetCall)(a,null,e);L({credential_name:t.credential_name,credential_values:t.credential_values,credential_info:t.credential_info})})(),t(),s(),r(),i()},[a,e]);let e_=async t=>{if(!a)return;let s={credential_name:t.credential_name,model_id:e,credential_info:{custom_llm_provider:p.litellm_params?.custom_llm_provider}};G.default.info("Storing credential.."),await (0,l.credentialCreateCall)(a,s),G.default.success("Credential stored successfully")},ej=async t=>{try{let s;if(!a)return;P(!0);let r={};try{r=t.litellm_extra_params?JSON.parse(t.litellm_extra_params):{},delete r.litellm_credential_name}catch(e){G.default.fromBackend("Invalid JSON in LiteLLM Params"),P(!1);return}let i={...t.litellm_params,...r,model:t.litellm_model_name,api_base:t.api_base,custom_llm_provider:t.custom_llm_provider,organization:t.organization,tpm:t.tpm,rpm:t.rpm,max_retries:t.max_retries,timeout:t.timeout,stream_timeout:t.stream_timeout,tags:t.tags};u.isFieldTouched("input_cost")&&(void 0!==t.input_cost&&null!==t.input_cost&&""!==t.input_cost?i.input_cost_per_token=Number(t.input_cost)/1e6:i.input_cost_per_token=null),u.isFieldTouched("output_cost")&&(void 0!==t.output_cost&&null!==t.output_cost&&""!==t.output_cost?i.output_cost_per_token=Number(t.output_cost)/1e6:i.output_cost_per_token=null),(u.isFieldTouched("cache_read_cost")||u.isFieldTouched("input_cost"))&&(void 0!==t.cache_read_cost&&null!==t.cache_read_cost&&""!==t.cache_read_cost?i.cache_read_input_token_cost=Number(t.cache_read_cost)/1e6:u.isFieldTouched("cache_read_cost")?i.cache_read_input_token_cost=null:void 0!==i.input_cost_per_token&&null!==i.input_cost_per_token&&(i.cache_read_input_token_cost=i.input_cost_per_token)),u.isFieldTouched("cache_write_cost")&&(void 0!==t.cache_write_cost&&null!==t.cache_write_cost&&""!==t.cache_write_cost?i.cache_creation_input_token_cost=Number(t.cache_write_cost)/1e6:i.cache_creation_input_token_cost=null),t.litellm_credential_name?i.litellm_credential_name=t.litellm_credential_name:delete i.litellm_credential_name,t.guardrails&&(i.guardrails=t.guardrails),t.vector_store_ids?.length>0?i.vector_store_ids=t.vector_store_ids:void 0!==t.vector_store_ids?i.vector_store_ids=[]:delete i.vector_store_ids,t.cache_control&&t.cache_control_injection_points?.length>0?i.cache_control_injection_points=t.cache_control_injection_points:delete i.cache_control_injection_points;try{s=t.model_info?JSON.parse(t.model_info):eh.model_info,t.model_access_group&&(s={...s,access_groups:t.model_access_group}),void 0!==t.health_check_model&&(s={...s,health_check_model:t.health_check_model})}catch(e){G.default.fromBackend("Invalid JSON in Model Info");return}let n=Object.fromEntries(Object.entries(i).filter(([,e])=>!li(e))),d={model_name:t.model_name,litellm_params:n,model_info:s};await (0,l.modelPatchUpdateCall)(a,d,e);let c={...p,model_name:t.model_name,litellm_model_name:t.litellm_model_name,litellm_params:n,model_info:s};g(c),o&&o(c),G.default.success("Model settings updated successfully"),T(!1),A(!1)}catch(e){console.error("Error updating model:",e),G.default.fromBackend("Failed to update model settings")}finally{P(!1)}};if(eo)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)(I.Button,{icon:t6.ArrowLeftIcon,variant:"light",onClick:s,className:"mb-4",children:"Back to Models"}),(0,t.jsx)(em.Text,{children:"Loading..."})]});if(!eh)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)(I.Button,{icon:t6.ArrowLeftIcon,variant:"light",onClick:s,className:"mb-4",children:"Back to Models"}),(0,t.jsx)(em.Text,{children:"Model not found"})]});let ey=async()=>{if(a)try{G.default.info("Testing connection...");let e=await (0,l.testConnectionRequest)(a,{custom_llm_provider:p.litellm_params.custom_llm_provider,litellm_credential_name:p.litellm_params.litellm_credential_name,model:p.litellm_model_name},{id:p.model_info?.id,mode:p.model_info?.mode},p.model_info?.mode);if("success"===e.status)G.default.success("Connection test successful!");else throw Error(e?.result?.error||e?.message||"Unknown error")}catch(e){e instanceof Error?G.default.error("Error testing connection: "+(0,tL.truncateString)(e.message,100)):G.default.error("Error testing connection: "+String(e))}},eb=async()=>{try{if(y(!0),!a)return;await (0,l.modelDeleteCall)(a,e),G.default.success("Model deleted successfully"),o&&o({deleted:!0,model_info:{id:e}}),s()}catch(e){console.error("Error deleting the model:",e),G.default.fromBackend("Failed to delete model")}finally{y(!1),_(!1)}},ev=async(e,t)=>{await (0,t9.copyToClipboard)(e)&&(q(e=>({...e,[t]:!0})),setTimeout(()=>{q(e=>({...e,[t]:!1}))},2e3))},eN=eh.litellm_model_name.includes("*");return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-6",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(I.Button,{icon:t6.ArrowLeftIcon,variant:"light",onClick:s,className:"mb-4",children:"Back to Models"}),(0,t.jsxs)(eu.Title,{children:["Public Model Name: ",D(eh)]}),(0,t.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,t.jsx)(em.Text,{className:"text-gray-500 font-mono",children:eh.model_info.id}),(0,t.jsx)(Q.Button,{type:"text",size:"small",icon:z["model-id"]?(0,t.jsx)(t8.CheckIcon,{size:12}):(0,t.jsx)(t7.CopyIcon,{size:12}),onClick:()=>ev(eh.model_info.id,"model-id"),className:`left-2 z-10 transition-all duration-200 ${z["model-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(Q.Button,{icon:(0,t.jsx)(e2.RefreshIcon,{className:"h-4 w-4"}),onClick:ey,className:"flex items-center gap-2","data-testid":"test-connection-button",children:"Test Connection"}),(0,t.jsx)(Q.Button,{icon:(0,t.jsx)(t3,{className:"h-4 w-4"}),onClick:()=>w(!0),className:"flex items-center",disabled:!ep,"data-testid":"update-api-key-button",children:"Update API Key"}),(0,t.jsx)(Q.Button,{icon:(0,t.jsx)(t3,{className:"h-4 w-4"}),onClick:()=>v(!0),className:"flex items-center",disabled:!ex,"data-testid":"reuse-credentials-button",children:"Re-use Credentials"}),(0,t.jsx)(Q.Button,{danger:!0,icon:(0,t.jsx)(S.TrashIcon,{className:"h-4 w-4"}),onClick:()=>_(!0),className:"flex items-center",disabled:!ep,"data-testid":"delete-model-button",children:"Delete Model"})]})]}),(0,t.jsxs)(e6.TabGroup,{children:[(0,t.jsxs)(e3.TabList,{className:"mb-6",children:[(0,t.jsx)(e5.Tab,{children:"Overview"}),(0,t.jsx)(e5.Tab,{children:"Raw JSON"})]}),(0,t.jsxs)(e8.TabPanels,{children:[(0,t.jsxs)(J.TabPanel,{children:[(0,t.jsxs)(K.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6 mb-6",children:[(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(em.Text,{children:"Provider"}),(0,t.jsxs)("div",{className:"mt-2 flex items-center space-x-2",children:[eh.provider&&(0,t.jsx)("img",{src:(0,eP.getProviderLogoAndName)(eh.provider).logo,alt:`${eh.provider} logo`,className:"w-4 h-4",onError:e=>{let t=e.currentTarget,l=t.parentElement;if(l&&l.contains(t))try{let e=document.createElement("div");e.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",e.textContent=eh.provider?.charAt(0)||"-",l.replaceChild(e,t)}catch(e){console.error("Failed to replace provider logo fallback:",e)}}}),(0,t.jsx)(eu.Title,{children:eh.provider||"Not Set"})]})]}),(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(em.Text,{children:"LiteLLM Model"}),(0,t.jsx)("div",{className:"mt-2 overflow-hidden",children:(0,t.jsx)(O.Tooltip,{title:eh.litellm_model_name||"Not Set",children:(0,t.jsx)("div",{className:"break-all text-sm font-medium leading-relaxed cursor-pointer",children:eh.litellm_model_name||"Not Set"})})})]}),(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(em.Text,{children:"Pricing"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(em.Text,{children:["Input: $",eh.input_cost,"/1M tokens"]}),(0,t.jsxs)(em.Text,{children:["Output: $",eh.output_cost,"/1M tokens"]})]})]})]}),(0,t.jsxs)("div",{className:"mb-6 text-sm text-gray-500 flex items-center gap-x-6",children:[(0,t.jsxs)("div",{className:"flex items-center gap-x-2",children:[(0,t.jsx)("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"})}),"Created At"," ",eh.model_info.created_at?new Date(eh.model_info.created_at).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):"Not Set"]}),(0,t.jsxs)("div",{className:"flex items-center gap-x-2",children:[(0,t.jsx)("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"})}),"Created By ",eh.model_info.created_by||"Not Set"]})]}),(0,t.jsxs)(eL.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(eu.Title,{children:"Model Settings"}),(0,t.jsxs)("div",{className:"flex gap-2",children:[eg&&ep&&!M&&(0,t.jsx)(I.Button,{onClick:()=>U(!0),className:"flex items-center",children:"Edit Auto Router"}),ep?!M&&(0,t.jsx)(I.Button,{onClick:()=>A(!0),className:"flex items-center",children:"Edit Settings"}):(0,t.jsx)(O.Tooltip,{title:"Only DB models can be edited. You must be an admin or the creator of the model to edit it.",children:(0,t.jsx)(C.InfoCircleOutlined,{})})]})]}),p?(0,t.jsx)(el.Form,{form:u,onFinish:ej,initialValues:{model_name:p.model_name,litellm_model_name:p.litellm_model_name,api_base:p.litellm_params.api_base,custom_llm_provider:p.litellm_params.custom_llm_provider,organization:p.litellm_params.organization,tpm:p.litellm_params.tpm,rpm:p.litellm_params.rpm,max_retries:p.litellm_params.max_retries,timeout:p.litellm_params.timeout,stream_timeout:p.litellm_params.stream_timeout,input_cost:p.litellm_params.input_cost_per_token?1e6*p.litellm_params.input_cost_per_token:p.model_info?.input_cost_per_token*1e6||null,output_cost:p.litellm_params?.output_cost_per_token?1e6*p.litellm_params.output_cost_per_token:p.model_info?.output_cost_per_token*1e6||null,cache_read_cost:p.litellm_params?.cache_read_input_token_cost!==void 0&&p.litellm_params?.cache_read_input_token_cost!==null?1e6*p.litellm_params.cache_read_input_token_cost:p.model_info?.cache_read_input_token_cost!==void 0&&p.model_info?.cache_read_input_token_cost!==null?1e6*p.model_info.cache_read_input_token_cost:null,cache_write_cost:p.litellm_params?.cache_creation_input_token_cost!==void 0&&p.litellm_params?.cache_creation_input_token_cost!==null?1e6*p.litellm_params.cache_creation_input_token_cost:p.model_info?.cache_creation_input_token_cost!==void 0&&p.model_info?.cache_creation_input_token_cost!==null?1e6*p.model_info.cache_creation_input_token_cost:null,cache_control:!!p.litellm_params?.cache_control_injection_points,cache_control_injection_points:p.litellm_params?.cache_control_injection_points||[],model_access_group:Array.isArray(p.model_info?.access_groups)?p.model_info.access_groups:[],guardrails:Array.isArray(p.litellm_params?.guardrails)?p.litellm_params.guardrails:[],vector_store_ids:Array.isArray(p.litellm_params?.vector_store_ids)&&p.litellm_params.vector_store_ids.length>0?p.litellm_params.vector_store_ids:void 0,tags:Array.isArray(p.litellm_params?.tags)?p.litellm_params.tags:[],health_check_model:eN?p.model_info?.health_check_model:null,litellm_credential_name:p.litellm_params?.litellm_credential_name||"",litellm_extra_params:JSON.stringify(Object.fromEntries(Object.entries(p.litellm_params||{}).filter(([e,t])=>"litellm_credential_name"!==e&&!li(t))),null,2)},layout:"vertical",onValuesChange:()=>T(!0),children:(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Model Name"}),M?(0,t.jsx)(el.Form.Item,{name:"model_name",className:"mb-0",children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter model name"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.model_name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"LiteLLM Model Name"}),M?(0,t.jsx)(el.Form.Item,{name:"litellm_model_name",className:"mb-0",children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter LiteLLM model name"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_model_name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Input Cost (per 1M tokens)"}),M?(0,t.jsx)(el.Form.Item,{name:"input_cost",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter input cost"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p?.litellm_params?.input_cost_per_token?(p.litellm_params?.input_cost_per_token*1e6).toFixed(4):p?.model_info?.input_cost_per_token?(1e6*p.model_info.input_cost_per_token).toFixed(4):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Output Cost (per 1M tokens)"}),M?(0,t.jsx)(el.Form.Item,{name:"output_cost",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter output cost"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p?.litellm_params?.output_cost_per_token?(1e6*p.litellm_params.output_cost_per_token).toFixed(4):p?.model_info?.output_cost_per_token?(1e6*p.model_info.output_cost_per_token).toFixed(4):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Cache Read Cost (per 1M tokens)"}),M?(0,t.jsx)(el.Form.Item,{name:"cache_read_cost",className:"mb-0",tooltip:"If left blank on save, defaults to Input Cost.",children:(0,t.jsx)(tP.default,{placeholder:"Defaults to Input Cost if blank"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p?.litellm_params?.cache_read_input_token_cost!==void 0&&p?.litellm_params?.cache_read_input_token_cost!==null?(1e6*p.litellm_params.cache_read_input_token_cost).toFixed(4):p?.model_info?.cache_read_input_token_cost!==void 0&&p?.model_info?.cache_read_input_token_cost!==null?(1e6*p.model_info.cache_read_input_token_cost).toFixed(4):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Cache Write Cost (per 1M tokens)"}),M?(0,t.jsx)(el.Form.Item,{name:"cache_write_cost",className:"mb-0",tooltip:"If left blank on save, defaults to Input Cost (backend falls back to input_cost_per_token).",children:(0,t.jsx)(tP.default,{placeholder:"Defaults to Input Cost if blank"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p?.litellm_params?.cache_creation_input_token_cost!==void 0&&p?.litellm_params?.cache_creation_input_token_cost!==null?(1e6*p.litellm_params.cache_creation_input_token_cost).toFixed(4):p?.model_info?.cache_creation_input_token_cost!==void 0&&p?.model_info?.cache_creation_input_token_cost!==null?(1e6*p.model_info.cache_creation_input_token_cost).toFixed(4):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"API Base"}),M?(0,t.jsx)(el.Form.Item,{name:"api_base",className:"mb-0",children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter API base"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.api_base||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Custom LLM Provider"}),M?(0,t.jsx)(el.Form.Item,{name:"custom_llm_provider",className:"mb-0",children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter custom LLM provider"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.custom_llm_provider||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Organization"}),M?(0,t.jsx)(el.Form.Item,{name:"organization",className:"mb-0",children:(0,t.jsx)(eO.TextInput,{placeholder:"Enter organization"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.organization||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"TPM (Tokens per Minute)"}),M?(0,t.jsx)(el.Form.Item,{name:"tpm",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter TPM"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.tpm||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"RPM (Requests per Minute)"}),M?(0,t.jsx)(el.Form.Item,{name:"rpm",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter RPM"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.rpm||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Max Retries"}),M?(0,t.jsx)(el.Form.Item,{name:"max_retries",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter max retries"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.max_retries||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Timeout (seconds)"}),M?(0,t.jsx)(el.Form.Item,{name:"timeout",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter timeout"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.timeout||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Stream Timeout (seconds)"}),M?(0,t.jsx)(el.Form.Item,{name:"stream_timeout",className:"mb-0",children:(0,t.jsx)(tP.default,{placeholder:"Enter stream timeout"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.stream_timeout||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Model Access Groups"}),M?(0,t.jsx)(el.Form.Item,{name:"model_access_group",className:"mb-0",children:(0,t.jsx)(Y.Select,{mode:"tags",showSearch:!0,placeholder:"Select existing groups or type to create new ones",optionFilterProp:"children",tokenSeparators:[","],maxTagCount:"responsive",allowClear:!0,style:{width:"100%"},options:c?.map(e=>({value:e,label:e}))})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.model_info?.access_groups?Array.isArray(p.model_info.access_groups)?p.model_info.access_groups.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:p.model_info.access_groups.map((e,l)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800",children:e},l))}):"No groups assigned":p.model_info.access_groups:"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(em.Text,{className:"font-medium",children:["Guardrails",(0,t.jsx)(O.Tooltip,{title:"Apply safety guardrails to this model to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(C.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),M?(0,t.jsx)(el.Form.Item,{name:"guardrails",className:"mb-0",children:(0,t.jsx)(Y.Select,{mode:"tags",showSearch:!0,placeholder:"Select existing guardrails or type to create new ones",optionFilterProp:"children",tokenSeparators:[","],maxTagCount:"responsive",allowClear:!0,style:{width:"100%"},options:W.map(e=>({value:e,label:e}))})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.guardrails?Array.isArray(p.litellm_params.guardrails)?p.litellm_params.guardrails.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:p.litellm_params.guardrails.map((e,l)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800",children:e},l))}):"No guardrails assigned":p.litellm_params.guardrails:"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(em.Text,{className:"font-medium",children:["Attached Knowledge Bases (RAG)",(0,t.jsx)(O.Tooltip,{title:"Vector stores used for RAG. Every request to this model will automatically retrieve context from these knowledge bases.",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/completion/knowledgebase",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(C.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),M?(0,t.jsx)(el.Form.Item,{name:"vector_store_ids",className:"mb-0",children:(0,t.jsx)(tE.default,{onChange:()=>{},accessToken:a||"",placeholder:"Select knowledge bases (optional)"})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.vector_store_ids?Array.isArray(p.litellm_params.vector_store_ids)?p.litellm_params.vector_store_ids.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:p.litellm_params.vector_store_ids.map((e,l)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800",children:e},l))}):"No knowledge bases attached":String(p.litellm_params.vector_store_ids):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Tags"}),M?(0,t.jsx)(el.Form.Item,{name:"tags",className:"mb-0",children:(0,t.jsx)(Y.Select,{mode:"tags",showSearch:!0,placeholder:"Select existing tags or type to create new ones",optionFilterProp:"children",tokenSeparators:[","],maxTagCount:"responsive",allowClear:!0,style:{width:"100%"},options:Object.values(Z).map(e=>({value:e.name,label:e.name,title:e.description||e.name}))})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.tags?Array.isArray(p.litellm_params.tags)?p.litellm_params.tags.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:p.litellm_params.tags.map((e,l)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-800",children:e},l))}):"No tags assigned":p.litellm_params.tags:"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Existing Credentials"}),M?(0,t.jsx)(el.Form.Item,{name:"litellm_credential_name",className:"mb-0",children:(0,t.jsx)(Y.Select,{showSearch:!0,placeholder:"Select or search for existing credentials",optionFilterProp:"children",filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:[{value:"",label:"None"},...et.map(e=>({value:e.credential_name,label:e.credential_name}))],allowClear:!0})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.litellm_credential_name||"Manual"})]}),eN&&(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Health Check Model"}),M?(0,t.jsx)(el.Form.Item,{name:"health_check_model",className:"mb-0",children:(0,t.jsx)(Y.Select,{showSearch:!0,placeholder:"Select existing health check model",optionFilterProp:"children",allowClear:!0,options:(m=eh.litellm_model_name.split("/")[0],ed?.data?.filter(e=>e.providers?.includes(m)&&e.model_group!==eh.litellm_model_name).map(e=>({value:e.model_group,label:e.model_group}))||[])})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.model_info?.health_check_model||"Not Set"})]}),M?(0,t.jsx)(tA,{form:u,showCacheControl:R,onCacheControlChange:e=>B(e)}):(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Cache Control"}),(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:p.litellm_params?.cache_control_injection_points?(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{children:"Enabled"}),(0,t.jsx)("div",{className:"mt-2",children:p.litellm_params.cache_control_injection_points.map((e,l)=>(0,t.jsxs)("div",{className:"text-sm text-gray-600 mb-1",children:["Location: ",e.location,",",e.role&&(0,t.jsxs)("span",{children:[" Role: ",e.role]}),void 0!==e.index&&(0,t.jsxs)("span",{children:[" Index: ",e.index]})]},l))})]}):"Disabled"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Model Info"}),M?(0,t.jsx)(el.Form.Item,{name:"model_info",className:"mb-0",children:(0,t.jsx)(eV.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}',defaultValue:JSON.stringify(eh.model_info,null,2)})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:(0,t.jsx)("pre",{className:"bg-gray-100 p-2 rounded-sm text-xs overflow-auto mt-1",children:JSON.stringify(p.model_info,null,2)})})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(em.Text,{className:"font-medium",children:["LiteLLM Params",(0,t.jsx)(O.Tooltip,{title:"Optional litellm params used for making a litellm.completion() call. Some params are automatically added by LiteLLM.",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(C.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),M?(0,t.jsx)(el.Form.Item,{name:"litellm_extra_params",rules:[{validator:tL.formItemValidateJSON}],children:(0,t.jsx)(eV.Input.TextArea,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}):(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:(0,t.jsx)("pre",{className:"bg-gray-100 p-2 rounded-sm text-xs overflow-auto mt-1",children:JSON.stringify(p.litellm_params,null,2)})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Team ID"}),(0,t.jsx)("div",{className:"mt-1 p-2 bg-gray-50 rounded-sm",children:eh.model_info.team_id||"Not Set"})]})]}),M&&(0,t.jsxs)("div",{className:"mt-6 flex justify-end gap-2",children:[(0,t.jsx)(I.Button,{variant:"secondary",onClick:()=>{u.resetFields(),T(!1),A(!1)},disabled:F,children:"Cancel"}),(0,t.jsx)(I.Button,{variant:"primary",onClick:()=>u.submit(),loading:F,children:"Save Changes"})]})]})}):(0,t.jsx)(em.Text,{children:"Loading..."})]})]}),(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(eL.Card,{children:(0,t.jsx)("pre",{className:"bg-gray-100 p-4 rounded-sm text-xs overflow-auto",children:JSON.stringify(eh,null,2)})})})]})]}),(0,t.jsx)(H.default,{isOpen:f,title:"Delete Model",alertMessage:"This action cannot be undone.",message:"Are you sure you want to delete this model?",resourceInformationTitle:"Model Information",resourceInformation:[{label:"Model Name",value:eh?.model_name||"Not Set"},{label:"LiteLLM Model Name",value:eh?.litellm_model_name||"Not Set"},{label:"Provider",value:eh?.provider||"Not Set"},{label:"Created By",value:eh?.model_info?.created_by||"Not Set"}],onCancel:()=>_(!1),onOk:eb,confirmLoading:j}),b&&!ef?(0,t.jsx)(ls,{isVisible:b,onCancel:()=>v(!1),onAddCredential:e_,existingCredential:E,setIsCredentialModalOpen:v}):(0,t.jsx)(es.Modal,{open:b,onCancel:()=>v(!1),title:"Using Existing Credential",children:(0,t.jsx)(em.Text,{children:eh.litellm_params.litellm_credential_name})}),N&&a&&(0,t.jsx)(lr,{open:N,onCancel:()=>w(!1),accessToken:a,modelId:e,onUpdated:()=>{h.invalidateQueries({queryKey:["models","list"]})}}),(0,t.jsx)(le,{isVisible:V,onCancel:()=>U(!1),onSuccess:e=>{g(e),o&&o(e)},modelData:p||eh,accessToken:a||"",userRole:i||""})]})}var ln=e.i(37091),ld=e.i(218129);let lc=({value:e={},onChange:l})=>{let[s,a]=(0,x.useState)(Object.entries(e)),r=(e,t,r)=>{let i=[...s];i[e]=[t,r],a(i),l?.(Object.fromEntries(i))};return(0,t.jsxs)("div",{children:[s.map(([e,i],o)=>(0,t.jsxs)(E.Space,{style:{display:"flex",marginBottom:8},align:"center",children:[(0,t.jsx)(eO.TextInput,{placeholder:"Header Name",value:e,onChange:e=>r(o,e.target.value,i)}),(0,t.jsx)(eO.TextInput,{placeholder:"Header Value",value:i,onChange:t=>r(o,e,t.target.value)}),(0,t.jsx)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%"},children:(0,t.jsx)(tF.MinusCircleOutlined,{onClick:()=>{let e;a(e=s.filter((e,t)=>t!==o)),l?.(Object.fromEntries(e))},style:{cursor:"pointer"}})})]},o)),(0,t.jsx)(Q.Button,{type:"dashed",onClick:()=>{a([...s,["",""]])},icon:(0,t.jsx)(to.PlusOutlined,{}),children:"Add Header"})]})},lm=({value:e={},onChange:l})=>{let[s,a]=(0,x.useState)(Object.entries(e)),r=(e,t,r)=>{let i=[...s];i[e]=[t,r],a(i),l?.(Object.fromEntries(i))};return(0,t.jsxs)("div",{children:[s.map(([e,i],o)=>(0,t.jsxs)(E.Space,{style:{display:"flex",marginBottom:8},align:"center",children:[(0,t.jsx)(eO.TextInput,{placeholder:"Parameter Name (e.g., version)",value:e,onChange:e=>r(o,e.target.value,i)}),(0,t.jsx)(eO.TextInput,{placeholder:"Parameter Value (e.g., v1)",value:i,onChange:t=>r(o,e,t.target.value)}),(0,t.jsx)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%"},children:(0,t.jsx)(tF.MinusCircleOutlined,{onClick:()=>{let e;a(e=s.filter((e,t)=>t!==o)),l?.(Object.fromEntries(e))},style:{cursor:"pointer"}})})]},o)),(0,t.jsx)(Q.Button,{type:"dashed",onClick:()=>{a([...s,["",""]])},icon:(0,t.jsx)(to.PlusOutlined,{}),children:"Add Query Parameter"})]})};var lu=e.i(240647);let{Title:lh,Text:lp}=R.Typography,lx=({pathValue:e,targetValue:s,includeSubpath:a})=>{let r=(0,l.getProxyBaseUrl)();return e&&s?(0,t.jsxs)(e_.Card,{className:"p-5",children:[(0,t.jsx)(lh,{level:5,className:"text-lg font-semibold text-gray-900 mb-2",children:"Route Preview"}),(0,t.jsx)(lp,{type:"secondary",className:"text-gray-600 mb-5",style:{display:"block"},children:"How your requests will be routed"}),(0,t.jsxs)("div",{className:"space-y-5",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-base font-semibold text-gray-900 mb-3",children:"Basic routing:"}),(0,t.jsxs)("div",{className:"flex items-center gap-4",children:[(0,t.jsxs)("div",{className:"flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,t.jsx)("div",{className:"text-sm text-gray-600 mb-2",children:"Your endpoint"}),(0,t.jsx)("code",{className:"font-mono text-sm text-gray-900",children:e?`${r}${e}`:""})]}),(0,t.jsx)("div",{className:"text-gray-400",children:(0,t.jsx)(lu.RightOutlined,{className:"text-lg"})}),(0,t.jsxs)("div",{className:"flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,t.jsx)("div",{className:"text-sm text-gray-600 mb-2",children:"Forwards to"}),(0,t.jsx)("code",{className:"font-mono text-sm text-gray-900",children:s})]})]})]}),a&&(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-base font-semibold text-gray-900 mb-3",children:"With subpaths:"}),(0,t.jsxs)("div",{className:"flex items-center gap-4",children:[(0,t.jsxs)("div",{className:"flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,t.jsx)("div",{className:"text-sm text-gray-600 mb-2",children:"Your endpoint + subpath"}),(0,t.jsxs)("code",{className:"font-mono text-sm text-gray-900",children:[e&&`${r}${e}`,(0,t.jsx)("span",{className:"text-blue-600",children:"/v1/text-to-image/base/model"})]})]}),(0,t.jsx)("div",{className:"text-gray-400",children:(0,t.jsx)(lu.RightOutlined,{className:"text-lg"})}),(0,t.jsxs)("div",{className:"flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,t.jsx)("div",{className:"text-sm text-gray-600 mb-2",children:"Forwards to"}),(0,t.jsxs)("code",{className:"font-mono text-sm text-gray-900",children:[s,(0,t.jsx)("span",{className:"text-blue-600",children:"/v1/text-to-image/base/model"})]})]})]}),(0,t.jsxs)("div",{className:"mt-3 text-sm text-gray-600",children:["Any path after ",e," will be appended to the target URL"]})]})}),!a&&(0,t.jsx)("div",{className:"mt-4 p-3 bg-blue-50 rounded-md border border-blue-200",children:(0,t.jsxs)("div",{className:"flex items-start",children:[(0,t.jsx)(C.InfoCircleOutlined,{className:"text-blue-500 mt-0.5 mr-2 shrink-0"}),(0,t.jsxs)("div",{className:"text-sm text-blue-700",children:[(0,t.jsx)("span",{className:"font-medium",children:"Not seeing the routing you wanted?"})," Try enabling - Include Subpaths - above - this allows subroutes like"," ",(0,t.jsx)("code",{className:"bg-blue-100 px-1 py-0.5 rounded-sm font-mono text-xs",children:"/api/v1/models"})," to be forwarded automatically."]})]})})]})]}):null},lg=({premiumUser:e,authEnabled:l,onAuthChange:s})=>(0,t.jsxs)(eL.Card,{className:"p-6",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Security"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-4",children:"When enabled, requests to this endpoint will require a valid LiteLLM Virtual Key"}),e?(0,t.jsx)(el.Form.Item,{name:"auth",valuePropName:"checked",className:"mb-0",children:(0,t.jsx)(L.Switch,{checked:l,onChange:e=>{s(e)}})}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex items-center mb-3",children:[(0,t.jsx)(L.Switch,{disabled:!0,checked:!1,style:{outline:"2px solid #d1d5db",outlineOffset:"2px"}}),(0,t.jsx)("span",{className:"ml-2 text-sm text-gray-400",children:"Authentication (Premium)"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(em.Text,{className:"text-sm text-yellow-800",children:["Setting authentication for pass-through endpoints is a LiteLLM Enterprise feature. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})]});var lf=e.i(891547);let l_=({accessToken:e,value:l={},onChange:s,disabled:a=!1})=>{let[r,i]=(0,x.useState)(Object.keys(l)),[o,n]=(0,x.useState)(l);(0,x.useEffect)(()=>{n(l),i(Object.keys(l))},[l]);let d=(e,t,l)=>{let a=o[e]||{},r={...o,[e]:{...a,[t]:l.length>0?l:void 0}};r[e]?.request_fields||r[e]?.response_fields||(r[e]=null),n(r),s&&s(r)};return(0,t.jsxs)(eL.Card,{className:"p-6",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Guardrails"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-6",children:"Configure guardrails to enforce policies on requests and responses. Guardrails are opt-in for passthrough endpoints."}),(0,t.jsx)(tw.Alert,{message:(0,t.jsxs)("span",{children:["Field-Level Targeting"," ",(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through_guardrails#field-level-targeting",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"(Learn More)"})]}),description:(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("div",{children:"Optionally specify which fields to check. If left empty, the entire request/response is sent to the guardrail."}),(0,t.jsxs)("div",{className:"text-xs space-y-1 mt-2",children:[(0,t.jsx)("div",{className:"font-medium",children:"Common Examples:"}),(0,t.jsxs)("div",{children:["• ",(0,t.jsx)("code",{className:"bg-gray-100 px-1 rounded-sm",children:"query"})," - Single field"]}),(0,t.jsxs)("div",{children:["• ",(0,t.jsx)("code",{className:"bg-gray-100 px-1 rounded-sm",children:"documents[*].text"})," - All text in documents array"]}),(0,t.jsxs)("div",{children:["• ",(0,t.jsx)("code",{className:"bg-gray-100 px-1 rounded-sm",children:"messages[*].content"})," - All message contents"]})]})]}),type:"info",showIcon:!0,className:"mb-4"}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Select Guardrails",(0,t.jsx)(O.Tooltip,{title:"Choose which guardrails should run on this endpoint. Org/team/key level guardrails will also be included.",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),children:(0,t.jsx)(lf.default,{accessToken:e,value:r,onChange:e=>{i(e);let t={};e.forEach(e=>{t[e]=o[e]||null}),n(t),s&&s(t)},disabled:a})}),r.length>0&&(0,t.jsxs)("div",{className:"mt-6 space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Field Targeting (Optional)"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"💡 Tip: Leave empty to check entire payload"})]}),r.map(e=>(0,t.jsxs)(eL.Card,{className:"p-4 bg-gray-50",children:[(0,t.jsx)("div",{className:"text-sm font-medium text-gray-900 mb-3",children:e}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,t.jsxs)("label",{className:"text-xs text-gray-600 flex items-center",children:["Request Fields (pre_call)",(0,t.jsx)(O.Tooltip,{title:(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"font-medium mb-1",children:"Specify which request fields to check"}),(0,t.jsxs)("div",{className:"text-xs space-y-1",children:[(0,t.jsx)("div",{children:"Examples:"}),(0,t.jsx)("div",{children:"• query"}),(0,t.jsx)("div",{children:"• documents[*].text"}),(0,t.jsx)("div",{children:"• messages[*].content"})]})]}),children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-1 text-gray-400"})})]}),(0,t.jsxs)("div",{className:"flex gap-1",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{let t=o[e]?.request_fields||[];d(e,"request_fields",[...t,"query"])},className:"text-xs px-2 py-1 bg-white border border-gray-300 rounded-sm hover:bg-gray-50",disabled:a,children:"+ query"}),(0,t.jsx)("button",{type:"button",onClick:()=>{let t=o[e]?.request_fields||[];d(e,"request_fields",[...t,"documents[*]"])},className:"text-xs px-2 py-1 bg-white border border-gray-300 rounded-sm hover:bg-gray-50",disabled:a,children:"+ documents[*]"})]})]}),(0,t.jsx)(Y.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type field name or use + buttons above (e.g., query, documents[*].text)",value:o[e]?.request_fields||[],onChange:t=>d(e,"request_fields",t),disabled:a,tokenSeparators:[","]})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,t.jsxs)("label",{className:"text-xs text-gray-600 flex items-center",children:["Response Fields (post_call)",(0,t.jsx)(O.Tooltip,{title:(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"font-medium mb-1",children:"Specify which response fields to check"}),(0,t.jsxs)("div",{className:"text-xs space-y-1",children:[(0,t.jsx)("div",{children:"Examples:"}),(0,t.jsx)("div",{children:"• results[*].text"}),(0,t.jsx)("div",{children:"• choices[*].message.content"})]})]}),children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-1 text-gray-400"})})]}),(0,t.jsx)("div",{className:"flex gap-1",children:(0,t.jsx)("button",{type:"button",onClick:()=>{let t=o[e]?.response_fields||[];d(e,"response_fields",[...t,"results[*]"])},className:"text-xs px-2 py-1 bg-white border border-gray-300 rounded-sm hover:bg-gray-50",disabled:a,children:"+ results[*]"})})]}),(0,t.jsx)(Y.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type field name or use + buttons above (e.g., results[*].text)",value:o[e]?.response_fields||[],onChange:t=>d(e,"response_fields",t),disabled:a,tokenSeparators:[","]})]})]})]},e))]})]})},{Option:lj}=Y.Select,ly=["GET","POST","PUT","DELETE","PATCH"],lb=({accessToken:e,setPassThroughItems:s,passThroughItems:a,premiumUser:r=!1})=>{let[i]=el.Form.useForm(),[o,n]=(0,x.useState)(!1),[d,c]=(0,x.useState)(!1),[m,u]=(0,x.useState)(""),[h,p]=(0,x.useState)(""),[g,f]=(0,x.useState)(""),[_,j]=(0,x.useState)(!0),[y,b]=(0,x.useState)(!1),[v,N]=(0,x.useState)([]),[w,k]=(0,x.useState)({}),S=()=>{i.resetFields(),p(""),f(""),j(!0),N([]),k({}),n(!1)},T=async t=>{c(!0);try{!r&&"auth"in t&&delete t.auth,w&&Object.keys(w).length>0&&(t.guardrails=w),v&&v.length>0&&(t.methods=v);let o=(await (0,l.createPassThroughEndpoint)(e,t)).endpoints[0],d=[...a,o];s(d),G.default.success("Pass-through endpoint created successfully"),i.resetFields(),p(""),f(""),j(!0),N([]),k({}),n(!1)}catch(e){G.default.fromBackend("Error creating pass-through endpoint: "+e)}finally{c(!1)}};return(0,t.jsxs)("div",{children:[(0,t.jsx)(I.Button,{className:"mx-auto mb-4 mt-4",onClick:()=>n(!0),children:"+ Add Pass-Through Endpoint"}),(0,t.jsx)(es.Modal,{title:(0,t.jsxs)("div",{className:"flex items-center space-x-3 pb-4 border-b border-gray-100",children:[(0,t.jsx)(ld.ApiOutlined,{className:"text-xl text-blue-500"}),(0,t.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:"Add Pass-Through Endpoint"})]}),open:o,width:1e3,onCancel:S,footer:null,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:(0,t.jsxs)("div",{className:"mt-6",children:[(0,t.jsx)(tw.Alert,{message:"What is a Pass-Through Endpoint?",description:"Route requests from your LiteLLM proxy to any external API. Perfect for custom models, image generation APIs, or any service you want to proxy through LiteLLM.",type:"info",showIcon:!0,className:"mb-6"}),(0,t.jsxs)(el.Form,{form:i,onFinish:T,layout:"vertical",className:"space-y-6",initialValues:{include_subpath:!0,path:h,target:g},children:[(0,t.jsxs)(eL.Card,{className:"p-5",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Route Configuration"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-5",children:"Configure how requests to your domain will be forwarded to the target API"}),(0,t.jsxs)("div",{className:"space-y-5",children:[(0,t.jsx)(el.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Path Prefix"}),name:"path",rules:[{required:!0,message:"Path is required",pattern:/^\//}],extra:(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-1",children:"Example: /bria, /adobe-photoshop, /elasticsearch"}),className:"mb-4",children:(0,t.jsx)("div",{className:"flex items-center",children:(0,t.jsx)(eO.TextInput,{placeholder:"bria",value:h,onChange:e=>{var t;let l;return l=t=e.target.value,void(t&&!t.startsWith("/")&&(l="/"+t),p(l),i.setFieldsValue({path:l}))},className:"flex-1"})})}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Target URL"}),name:"target",rules:[{required:!0,message:"Target URL is required"},{type:"url",message:"Please enter a valid URL"}],extra:(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-1",children:"Example:https://engine.prod.bria-api.com"}),className:"mb-4",children:(0,t.jsx)(eO.TextInput,{placeholder:"https://engine.prod.bria-api.com",value:g,onChange:e=>{f(e.target.value),i.setFieldsValue({target:e.target.value})}})}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["HTTP Methods (Optional)",(0,t.jsx)(O.Tooltip,{title:"Select specific HTTP methods. Leave empty to support all methods (GET, POST, PUT, DELETE, PATCH). Useful when the same path needs different targets for different methods.",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),name:"methods",extra:(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-1",children:0===v.length?"All HTTP methods supported (default)":`Only ${v.join(", ")} requests will be routed to this endpoint`}),className:"mb-4",children:(0,t.jsx)(Y.Select,{mode:"multiple",placeholder:"Select methods (leave empty for all)",value:v,onChange:N,allowClear:!0,style:{width:"100%"},children:ly.map(e=>(0,t.jsx)(lj,{value:e,children:e},e))})}),(0,t.jsxs)("div",{className:"flex items-center justify-between py-3",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Include Subpaths"}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-0.5",children:"Forward all subpaths to the target API (recommended for REST APIs)"})]}),(0,t.jsx)(el.Form.Item,{name:"include_subpath",valuePropName:"checked",className:"mb-0",children:(0,t.jsx)(tN.Switch,{checked:_,onChange:j})})]})]})]}),(0,t.jsx)(lx,{pathValue:h,targetValue:g,includeSubpath:_}),(0,t.jsxs)(eL.Card,{className:"p-6",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Headers"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-6",children:"Add headers that will be sent with every request to the target API"}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Authentication Headers",(0,t.jsx)(O.Tooltip,{title:"Authentication and other headers to forward with requests",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),name:"headers",rules:[{required:!0,message:"Please configure the headers"}],extra:(0,t.jsxs)("div",{className:"text-xs text-gray-500 mt-2",children:[(0,t.jsx)("div",{className:"font-medium mb-1",children:"Add authentication tokens and other required headers"}),(0,t.jsx)("div",{children:"Common examples: auth_token, Authorization, x-api-key"})]}),children:(0,t.jsx)(lc,{})})]}),(0,t.jsxs)(eL.Card,{className:"p-6",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Default Query Parameters"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-6",children:"Add query parameters that will be automatically sent with every request to the target API"}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Default Query Parameters (Optional)",(0,t.jsx)(O.Tooltip,{title:"Query parameters that will be added to all requests. Clients can override these by providing their own values.",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),name:"default_query_params",extra:(0,t.jsxs)("div",{className:"text-xs text-gray-500 mt-2",children:[(0,t.jsx)("div",{className:"font-medium mb-1",children:"Parameters are sent with all GET, POST, PUT, PATCH requests"}),(0,t.jsx)("div",{children:"Client parameters override defaults. Examples: version=v1, format=json, key=default"})]}),children:(0,t.jsx)(lm,{})})]}),(0,t.jsx)(lg,{premiumUser:r,authEnabled:y,onAuthChange:e=>{b(e),i.setFieldsValue({auth:e})}}),(0,t.jsx)(l_,{accessToken:e,value:w,onChange:k}),(0,t.jsxs)(eL.Card,{className:"p-6",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Performance"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-6",children:"Configure upstream request timeout for this endpoint"}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Request Timeout (seconds)",(0,t.jsx)(O.Tooltip,{title:"Max time to wait for the upstream API to respond. Leave empty to use general_settings.pass_through_request_timeout (default 600s).",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-2 text-gray-400 hover:text-gray-600"})})]}),name:"timeout",extra:(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"Use a higher value for slow upstream APIs (e.g. 1200 for long-running LLM calls)"}),children:(0,t.jsx)(tP.default,{min:1,step:1,precision:0,placeholder:"600",size:"large"})})]}),(0,t.jsxs)(eL.Card,{className:"p-6",children:[(0,t.jsx)(eu.Title,{className:"text-lg font-semibold text-gray-900 mb-2",children:"Billing"}),(0,t.jsx)(ln.Subtitle,{className:"text-gray-600 mb-6",children:"Optional cost tracking for this endpoint"}),(0,t.jsx)(el.Form.Item,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Cost Per Request (USD)",(0,t.jsx)(O.Tooltip,{title:"Optional: Track costs for requests to this endpoint",children:(0,t.jsx)(C.InfoCircleOutlined,{className:"ml-2 text-gray-400 hover:text-gray-600"})})]}),name:"cost_per_request",extra:(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"The cost charged for each request through this endpoint"}),children:(0,t.jsx)(tP.default,{min:0,step:.001,precision:4,placeholder:"2.0000",size:"large"})})]}),(0,t.jsxs)("div",{className:"flex items-center justify-end space-x-3 pt-6 border-t border-gray-100",children:[(0,t.jsx)(I.Button,{variant:"secondary",onClick:S,children:"Cancel"}),(0,t.jsx)(I.Button,{variant:"primary",loading:d,onClick:()=>{i.submit()},children:d?"Creating...":"Add Pass-Through Endpoint"})]})]})]})})]})};var lv=e.i(286536),lN=e.i(77705);let lw=["GET","POST","PUT","DELETE","PATCH"],{Option:lC}=Y.Select,lk=({value:e})=>{let[l,s]=(0,x.useState)(!1),a=JSON.stringify(e,null,2);return(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("pre",{className:"font-mono text-xs bg-gray-50 p-2 rounded-sm max-w-md overflow-auto",children:l?a:"••••••••"}),(0,t.jsx)("button",{onClick:()=>s(!l),className:"p-1 hover:bg-gray-100 rounded-sm",type:"button",children:l?(0,t.jsx)(lN.EyeOff,{className:"w-4 h-4 text-gray-500"}):(0,t.jsx)(lv.Eye,{className:"w-4 h-4 text-gray-500"})})]})},lS=({endpointData:e,onClose:s,accessToken:a,isAdmin:r,premiumUser:i=!1,onEndpointUpdated:o})=>{let[n,d]=(0,x.useState)(e),[c,m]=(0,x.useState)(!1),[u,h]=(0,x.useState)(!1),[p,g]=(0,x.useState)(e?.auth||!1),[f,_]=(0,x.useState)(e?.methods||[]),[j,y]=(0,x.useState)(e?.guardrails||{}),[b]=el.Form.useForm(),v=async e=>{try{if(!a||!n?.id)return;let t={};if(e.headers)try{t="string"==typeof e.headers?JSON.parse(e.headers):e.headers}catch(e){G.default.fromBackend("Invalid JSON format for headers");return}let s={path:n.path,target:e.target,headers:t,include_subpath:e.include_subpath,cost_per_request:e.cost_per_request,timeout:e.timeout,auth:i?e.auth:void 0,methods:f&&f.length>0?f:void 0,guardrails:j&&Object.keys(j).length>0?j:void 0};await (0,l.updatePassThroughEndpoint)(a,n.id,s),d({...n,...s}),h(!1),o&&o()}catch(e){console.error("Error updating endpoint:",e),G.default.fromBackend("Failed to update pass through endpoint")}},N=async()=>{try{if(!a||!n?.id)return;await (0,l.deletePassThroughEndpointsCall)(a,n.id),G.default.success("Pass through endpoint deleted successfully"),s(),o&&o()}catch(e){console.error("Error deleting endpoint:",e),G.default.fromBackend("Failed to delete pass through endpoint")}};return c?(0,t.jsx)("div",{className:"p-4",children:"Loading..."}):n?(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)("div",{className:"flex justify-between items-center mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(Q.Button,{onClick:s,className:"mb-4",children:"← Back"}),(0,t.jsxs)(eu.Title,{children:["Pass Through Endpoint: ",n.path]}),(0,t.jsx)(em.Text,{className:"text-gray-500 font-mono",children:n.id})]})}),(0,t.jsxs)(e6.TabGroup,{children:[(0,t.jsxs)(e3.TabList,{className:"mb-4",children:[(0,t.jsx)(e5.Tab,{children:"Overview"},"overview"),r?(0,t.jsx)(e5.Tab,{children:"Settings"},"settings"):(0,t.jsx)(t.Fragment,{})]}),(0,t.jsxs)(e8.TabPanels,{children:[(0,t.jsxs)(J.TabPanel,{children:[(0,t.jsxs)(K.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(em.Text,{children:"Path"}),(0,t.jsx)("div",{className:"mt-2",children:(0,t.jsx)(eu.Title,{className:"font-mono",children:n.path})})]}),(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(em.Text,{children:"Target"}),(0,t.jsx)("div",{className:"mt-2",children:(0,t.jsx)(eu.Title,{children:n.target})})]}),(0,t.jsxs)(eL.Card,{children:[(0,t.jsx)(em.Text,{children:"Configuration"}),(0,t.jsxs)("div",{className:"mt-2 space-y-2",children:[(0,t.jsx)("div",{children:(0,t.jsx)(T.Badge,{color:n.include_subpath?"green":"gray",children:n.include_subpath?"Include Subpath":"Exact Path"})}),(0,t.jsx)("div",{children:(0,t.jsx)(T.Badge,{color:n.auth?"blue":"gray",children:n.auth?"Auth Required":"No Auth"})}),n.methods&&n.methods.length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"text-xs text-gray-500",children:"HTTP Methods:"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-1 mt-1",children:n.methods.map(e=>(0,t.jsx)(T.Badge,{color:"indigo",size:"sm",children:e},e))})]}),(!n.methods||0===n.methods.length)&&(0,t.jsx)("div",{children:(0,t.jsx)(em.Text,{className:"text-xs text-gray-500",children:"All HTTP methods supported"})}),void 0!==n.cost_per_request&&(0,t.jsx)("div",{children:(0,t.jsxs)(em.Text,{children:["Cost per request: $",n.cost_per_request]})})]})]})]}),(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(lx,{pathValue:n.path,targetValue:n.target,includeSubpath:n.include_subpath||!1})}),n.headers&&Object.keys(n.headers).length>0&&(0,t.jsxs)(eL.Card,{className:"mt-6",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Headers"}),(0,t.jsxs)(T.Badge,{color:"blue",children:[Object.keys(n.headers).length," headers configured"]})]}),(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(lk,{value:n.headers})})]}),n.guardrails&&Object.keys(n.guardrails).length>0&&(0,t.jsxs)(eL.Card,{className:"mt-6",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Guardrails"}),(0,t.jsxs)(T.Badge,{color:"purple",children:[Object.keys(n.guardrails).length," guardrails configured"]})]}),(0,t.jsx)("div",{className:"mt-4 space-y-2",children:Object.entries(n.guardrails).map(([e,l])=>(0,t.jsxs)("div",{className:"p-3 bg-gray-50 rounded-sm",children:[(0,t.jsx)("div",{className:"font-medium text-sm",children:e}),l&&(l.request_fields||l.response_fields)&&(0,t.jsxs)("div",{className:"mt-2 text-xs text-gray-600 space-y-1",children:[l.request_fields&&(0,t.jsxs)("div",{children:["Request fields: ",l.request_fields.join(", ")]}),l.response_fields&&(0,t.jsxs)("div",{children:["Response fields: ",l.response_fields.join(", ")]})]}),!l&&(0,t.jsx)("div",{className:"text-xs text-gray-600 mt-1",children:"Uses entire payload"})]},e))})]})]}),r&&(0,t.jsx)(J.TabPanel,{children:(0,t.jsxs)(eL.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(eu.Title,{children:"Pass Through Endpoint Settings"}),(0,t.jsx)("div",{className:"space-x-2",children:!u&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(I.Button,{onClick:()=>h(!0),children:"Edit Settings"}),(0,t.jsx)(I.Button,{onClick:N,variant:"secondary",color:"red",children:"Delete Endpoint"})]})})]}),u?(0,t.jsxs)(el.Form,{form:b,onFinish:v,initialValues:{target:n.target,headers:n.headers?JSON.stringify(n.headers,null,2):"",include_subpath:n.include_subpath||!1,cost_per_request:n.cost_per_request,timeout:n.timeout,auth:n.auth||!1,methods:n.methods||[]},layout:"vertical",children:[(0,t.jsx)(el.Form.Item,{label:"Target URL",name:"target",rules:[{required:!0,message:"Please input a target URL"}],children:(0,t.jsx)(eO.TextInput,{placeholder:"https://api.example.com"})}),(0,t.jsx)(el.Form.Item,{label:"Headers (JSON)",name:"headers",children:(0,t.jsx)(eV.Input.TextArea,{rows:5,placeholder:'{"Authorization": "Bearer your-token", "Content-Type": "application/json"}'})}),(0,t.jsx)(el.Form.Item,{label:"HTTP Methods (Optional)",name:"methods",extra:0===f.length?"All HTTP methods supported (default)":`Only ${f.join(", ")} requests will be routed to this endpoint`,children:(0,t.jsx)(Y.Select,{mode:"multiple",placeholder:"Select methods (leave empty for all)",value:f,onChange:_,allowClear:!0,style:{width:"100%"},children:lw.map(e=>(0,t.jsx)(lC,{value:e,children:e},e))})}),(0,t.jsx)(el.Form.Item,{label:"Include Subpath",name:"include_subpath",valuePropName:"checked",children:(0,t.jsx)(L.Switch,{})}),(0,t.jsx)(el.Form.Item,{label:"Cost per Request",name:"cost_per_request",children:(0,t.jsx)(eh.InputNumber,{min:0,step:.01,precision:2,placeholder:"0.00",addonBefore:"$"})}),(0,t.jsx)(el.Form.Item,{label:"Request Timeout (seconds)",name:"timeout",extra:"Max time to wait for upstream response. Leave empty to use the global pass_through_request_timeout (default 600s).",children:(0,t.jsx)(eh.InputNumber,{min:1,step:1,precision:0,placeholder:"600",style:{width:"100%"}})}),(0,t.jsx)(lg,{premiumUser:i,authEnabled:p,onAuthChange:e=>{g(e),b.setFieldsValue({auth:e})}}),(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(l_,{accessToken:a||"",value:j,onChange:y})}),(0,t.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,t.jsx)(Q.Button,{onClick:()=>h(!1),children:"Cancel"}),(0,t.jsx)(I.Button,{children:"Save Changes"})]})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Path"}),(0,t.jsx)("div",{className:"font-mono",children:n.path})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Target URL"}),(0,t.jsx)("div",{children:n.target})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Include Subpath"}),(0,t.jsx)(T.Badge,{color:n.include_subpath?"green":"gray",children:n.include_subpath?"Yes":"No"})]}),void 0!==n.cost_per_request&&(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Cost per Request"}),(0,t.jsxs)("div",{children:["$",n.cost_per_request]})]}),void 0!==n.timeout&&null!==n.timeout&&(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Request Timeout"}),(0,t.jsxs)("div",{children:[n.timeout,"s"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Authentication Required"}),(0,t.jsx)(T.Badge,{color:n.auth?"green":"gray",children:n.auth?"Yes":"No"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(em.Text,{className:"font-medium",children:"Headers"}),n.headers&&Object.keys(n.headers).length>0?(0,t.jsx)("div",{className:"mt-2",children:(0,t.jsx)(lk,{value:n.headers})}):(0,t.jsx)("div",{className:"text-gray-500",children:"No headers configured"})]})]})]})})]})]})]}):(0,t.jsx)("div",{className:"p-4",children:"Pass through endpoint not found"})};var lT=e.i(149121);let lI=({value:e})=>{let[l,s]=(0,x.useState)(!1),a=JSON.stringify(e);return(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("span",{className:"font-mono text-xs",children:l?a:"••••••••"}),(0,t.jsx)("button",{onClick:()=>s(!l),className:"p-1 hover:bg-gray-100 rounded-sm",type:"button",children:l?(0,t.jsx)(lN.EyeOff,{className:"w-4 h-4 text-gray-500"}):(0,t.jsx)(lv.Eye,{className:"w-4 h-4 text-gray-500"})})]})},lF=({accessToken:e,userRole:s,userID:a,modelData:r,premiumUser:i})=>{let[o,n]=(0,x.useState)([]),[d,c]=(0,x.useState)(null),[m,u]=(0,x.useState)(!1),[h,p]=(0,x.useState)(null);(0,x.useEffect)(()=>{e&&s&&a&&(0,l.getPassThroughEndpointsCall)(e).then(e=>{n(e.endpoints)})},[e,s,a]);let g=async e=>{p(e),u(!0)},f=async()=>{if(null!=h&&e){try{await (0,l.deletePassThroughEndpointsCall)(e,h);let t=o.filter(e=>e.id!==h);n(t),G.default.success("Endpoint deleted successfully.")}catch(e){console.error("Error deleting the endpoint:",e),G.default.fromBackend("Error deleting the endpoint: "+e)}u(!1),p(null)}},_=[{header:"ID",accessorKey:"id",cell:e=>(0,t.jsx)(O.Tooltip,{title:e.row.original.id,children:(0,t.jsx)("div",{className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left w-full truncate whitespace-nowrap cursor-pointer max-w-[15ch]",onClick:()=>e.row.original.id&&c(e.row.original.id),children:e.row.original.id})})},{header:"Path",accessorKey:"path"},{header:"Target",accessorKey:"target",cell:e=>(0,t.jsx)(em.Text,{children:e.getValue()})},{header:()=>(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,t.jsx)("span",{children:"Methods"}),(0,t.jsx)(O.Tooltip,{title:"HTTP methods supported by this endpoint",children:(0,t.jsx)(tQ.InformationCircleIcon,{className:"w-4 h-4 text-gray-400 cursor-help"})})]}),accessorKey:"methods",cell:e=>{let l=e.getValue();return l&&0!==l.length?(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:l.map(e=>(0,t.jsx)(W.Badge,{color:"indigo",className:"text-xs",children:e},e))}):(0,t.jsx)(W.Badge,{color:"blue",children:"ALL"})}},{header:()=>(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,t.jsx)("span",{children:"Authentication"}),(0,t.jsx)(O.Tooltip,{title:"LiteLLM Virtual Key required to call endpoint",children:(0,t.jsx)(tQ.InformationCircleIcon,{className:"w-4 h-4 text-gray-400 cursor-help"})})]}),accessorKey:"auth",cell:e=>(0,t.jsx)(W.Badge,{color:e.getValue()?"green":"gray",children:e.getValue()?"Yes":"No"})},{header:"Headers",accessorKey:"headers",cell:e=>(0,t.jsx)(lI,{value:e.getValue()||{}})},{header:"Actions",id:"actions",cell:({row:e})=>(0,t.jsxs)("div",{className:"flex space-x-1",children:[(0,t.jsx)(F.Icon,{icon:eE.PencilAltIcon,size:"sm",onClick:()=>e.original.id&&c(e.original.id),title:"Edit"}),(0,t.jsx)(F.Icon,{icon:S.TrashIcon,size:"sm",onClick:()=>{var t;return t=e.original.id,e.index,void g(t)},title:"Delete"})]})}];if(!e)return null;if(d){let a=o.find(e=>e.id===d);return a?(0,t.jsx)(lS,{endpointData:a,onClose:()=>c(null),accessToken:e,isAdmin:"Admin"===s||"admin"===s,premiumUser:i,onEndpointUpdated:()=>{e&&(0,l.getPassThroughEndpointsCall)(e).then(e=>{n(e.endpoints)})}}):(0,t.jsx)("div",{children:"Endpoint not found"})}return(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(eu.Title,{children:"Pass Through Endpoints"}),(0,t.jsx)(em.Text,{className:"text-tremor-content",children:"Configure and manage your pass-through endpoints"})]}),(0,t.jsx)(lb,{accessToken:e,setPassThroughItems:n,passThroughItems:o,premiumUser:i}),(0,t.jsx)(lT.DataTable,{data:o,columns:_,renderSubComponent:()=>(0,t.jsx)("div",{}),getRowCanExpand:()=>!1,isLoading:!1,noDataMessage:"No pass-through endpoints configured"}),m&&(0,t.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,t.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,t.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,t.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,t.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,t.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,t.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,t.jsx)("div",{className:"sm:flex sm:items-start",children:(0,t.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,t.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Pass-Through Endpoint"}),(0,t.jsx)("div",{className:"mt-2",children:(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this pass-through endpoint? This action cannot be undone."})})]})})}),(0,t.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,t.jsx)(I.Button,{onClick:f,color:"red",className:"ml-2",children:"Delete"}),(0,t.jsx)(I.Button,{onClick:()=>{u(!1),p(null)},children:"Cancel"})]})]})]})})]})};var lP=e.i(56567);let lM=({premiumUser:e,teams:s})=>{let a,i,{accessToken:u,token:h,userRole:p,userId:g}=(0,r.default)(),[f]=el.Form.useForm(),[_,j]=(0,x.useState)(""),[y,b]=(0,x.useState)([]),[v,N]=(0,x.useState)(eP.Providers.Anthropic),[w,C]=(0,x.useState)(null),[k,S]=(0,x.useState)("global"),[T,I]=(0,x.useState)(null),[P,M]=(0,x.useState)(null),[A,E]=(0,x.useState)(0),[L,O]=(0,x.useState)({}),[R,B]=(0,x.useState)(!1),[z,q]=(0,x.useState)(null),[V,H]=(0,x.useState)(null),[U,W]=(0,x.useState)(0),[Q,Y]=(0,x.useState)(1),[X,Z]=(0,x.useState)(()=>"true"!==localStorage.getItem("hideMissingProviderBanner")),ee=(0,$.useQueryClient)(),{data:et,isLoading:es,refetch:ea}=(0,d.useModelsInfo)(),{data:er,isLoading:eo}=(0,d.useModelsInfo)(Q,50),{data:ed,isLoading:ec}=(0,n.useModelCostMap)(),{data:em,isLoading:eu}=o(),eh=em?.credentials||[],{data:ep,isLoading:eg}=(0,c.useUISettings)(),ef=(0,m.useMutation)({mutationFn:async e=>{if(!u)throw Error("Access token is required");return(0,l.setCallbacksCall)(u,{router_settings:e})}}),e_=(0,x.useMemo)(()=>{if(!et?.data)return[];let e=new Set;for(let t of et.data)e.add(t.model_name);return Array.from(e).sort()},[et?.data]),ej=(0,x.useMemo)(()=>{if(!et?.data)return[];let e=new Set;for(let t of et.data){let l=t.model_info;if(l?.access_groups)for(let t of l.access_groups)e.add(t)}return Array.from(e)},[et?.data]),ey=(0,x.useMemo)(()=>et?.data?et.data.map(e=>e.model_name):[],[et?.data]),eb=(0,x.useMemo)(()=>er?.data?er.data.map(e=>e.model_info?.id).filter(e=>!!e):[],[er?.data]),ev=e=>null!=ed&&"object"==typeof ed&&e in ed?ed[e].litellm_provider:"openai",eN=(0,x.useMemo)(()=>et?.data?ei(et,ev):{data:[]},[et?.data,ev]),ew=(0,x.useMemo)(()=>er?.data?ei(er,ev):{data:[]},[er?.data,ev]),eC=(0,x.useMemo)(()=>({total_count:er?.total_count??0,current_page:er?.current_page??Q,total_pages:er?.total_pages??1,size:er?.size??50}),[er,Q]),ek=p&&(0,e0.isProxyAdminRole)(p),eS=p&&e0.internalUserRoles.includes(p),eT=g&&(0,e0.isUserTeamAdminForAnyTeam)(s,g),eI=eS&&ep?.values?.disable_model_add_for_internal_users===!0,eM={name:"file",accept:".json",pastable:!1,beforeUpload:e=>{if("application/json"===e.type){let t=new FileReader;t.onload=e=>{if(e.target){let t=e.target.result;f.setFieldsValue({vertex_credentials:t})}},t.readAsText(e)}return!1},onChange(e){"done"===e.file.status?G.default.success(`${e.file.name} file uploaded successfully`):"error"===e.file.status&&G.default.fromBackend(`${e.file.name} file upload failed.`)}},eE=()=>{j(new Date().toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})),Y(1),ee.invalidateQueries({queryKey:["models","list"]}),ea()},eL=(0,x.useCallback)(async()=>{if(!u||!g||!p)return null;try{return(await (0,l.getCallbacksCall)(u,g,p)).router_settings}catch(e){return console.error("Error fetching model data:",e),null}},[u,g,p]),eO=(0,x.useCallback)(e=>{I(e.model_group_retry_policy??null),M(e.retry_policy??null),E(e.num_retries??2),O(e.model_group_alias||{})},[]),eR=(0,x.useCallback)(async()=>{let e=await eL();e&&eO(e)},[eL,eO]);(0,x.useEffect)(()=>{if(!u||!h||!p||!g||!et)return;let e=!0;return(async()=>{let t=await eL();e&&t&&eO(t)})(),()=>{e=!1}},[u,h,p,g,et,eL,eO]);let eB=async()=>{try{let e=await f.validateFields();await eA(e,u,f,eE)}catch(t){let e=t.errorFields?.map(e=>`${e.name.join(".")}: ${e.errors.join(", ")}`).join(" | ")||"Unknown validation error";G.default.fromBackend(`Please fill in the following required fields: ${e}`)}};return(Object.keys(eP.Providers).find(e=>eP.Providers[e]===v),V)?(0,t.jsx)("div",{className:"w-full h-full",children:(0,t.jsx)(lP.default,{teamId:V,onClose:()=>H(null),accessToken:u,is_team_admin:"Admin"===p,is_proxy_admin:"Proxy Admin"===p,userModels:ey,editTeam:!1,onUpdate:eE,premiumUser:e})}):(0,t.jsx)("div",{className:"w-full mx-4 h-[75vh]",children:(0,t.jsx)(K.Grid,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,t.jsxs)(e4.Col,{numColSpan:1,className:"flex flex-col gap-2",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("h2",{className:"text-lg font-semibold",children:"Model Management"}),e0.all_admin_roles.includes(p)?(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"Add and manage models for the proxy"}):(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"Add models for teams you are an admin for."})]}),!X&&(0,t.jsxs)("a",{href:"https://models.litellm.ai/?request=true",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-[#6366f1] hover:text-[#5558e3] border border-[#6366f1] hover:border-[#5558e3] rounded-lg transition-colors",children:[(0,t.jsx)(e7.PlusCircleOutlined,{style:{fontSize:"12px"}}),"Request Provider"]})]}),X&&(0,t.jsxs)("div",{className:"mb-4 px-4 py-3 bg-blue-50 rounded-lg border border-blue-100 flex items-center gap-4",children:[(0,t.jsx)("div",{className:"shrink-0 w-10 h-10 bg-white rounded-full flex items-center justify-center border border-blue-200",children:(0,t.jsx)(e7.PlusCircleOutlined,{style:{fontSize:"18px",color:"#6366f1"}})}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("h4",{className:"text-gray-900 font-semibold text-sm m-0",children:"Missing a provider?"}),(0,t.jsx)("p",{className:"text-gray-500 text-xs m-0 mt-0.5",children:"The LiteLLM engineering team is constantly adding support for new LLM models, providers, endpoints. If you don't see the one you need, let us know and we'll prioritize it."})]}),(0,t.jsxs)("a",{href:"https://models.litellm.ai/?request=true",target:"_blank",rel:"noopener noreferrer",className:"shrink-0 inline-flex items-center gap-2 px-4 py-2 bg-[#6366f1] hover:bg-[#5558e3] text-white text-sm font-medium rounded-lg transition-colors",children:["Request Provider",(0,t.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"})})]}),(0,t.jsx)("button",{onClick:()=>{Z(!1),localStorage.setItem("hideMissingProviderBanner","true")},className:"shrink-0 p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full transition-colors","aria-label":"Dismiss banner",children:(0,t.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-5 w-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"})})})]}),z&&!(es||ec||eu||eg)?(0,t.jsx)(lo,{modelId:z,onClose:()=>{q(null)},accessToken:u,userID:g,userRole:p,onModelUpdate:e=>{ee.invalidateQueries({queryKey:["models","list"]}),eE()},modelAccessGroups:ej}):(a=e0.all_admin_roles.includes(p),i=[{tab:(0,t.jsx)(e5.Tab,{children:a?"All Models":"Your Models"},"all-models"),panel:(0,t.jsx)(en,{selectedModelGroup:w,setSelectedModelGroup:C,availableModelGroups:e_,availableModelAccessGroups:ej,setSelectedModelId:q,setSelectedTeamId:H},"all-models")}],(ek||!eI&&eT)&&i.push({tab:(0,t.jsx)(e5.Tab,{children:"Add Model"},"add-model"),panel:(0,t.jsx)(J.TabPanel,{className:"h-full",children:(0,t.jsx)(tK,{form:f,handleOk:eB,selectedProvider:v,setSelectedProvider:N,providerModels:y,setProviderModelsFn:e=>{b((0,eP.getProviderModels)(e,ed))},getPlaceholder:eP.getPlaceholder,uploadProps:eM,showAdvancedSettings:R,setShowAdvancedSettings:B,teams:s,credentials:eh,accessToken:u,userRole:p})},"add-model")}),a&&i.push({tab:(0,t.jsx)(e5.Tab,{children:"LLM Credentials"},"llm-credentials"),panel:(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(e1,{uploadProps:eM})},"llm-credentials")},{tab:(0,t.jsx)(e5.Tab,{children:"Pass-Through Endpoints"},"pass-through"),panel:(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(lF,{accessToken:u,userRole:p,userID:g,modelData:eN,premiumUser:e})},"pass-through")},{tab:(0,t.jsx)(e5.Tab,{children:"Health Status"},"health-status"),panel:(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(tZ,{accessToken:u,modelData:ew,all_models_on_proxy:eb,getDisplayModelName:D,setSelectedModelId:q,teams:s,isLoading:eo,paginationMeta:eC,currentPage:Q,pageSize:50,onPageChange:Y})},"health-status")},{tab:(0,t.jsx)(e5.Tab,{children:"Model Retry Settings"},"model-retry-settings"),panel:(0,t.jsx)(ex,{selectedModelGroup:k,setSelectedModelGroup:S,availableModelGroups:e_,globalRetryPolicy:P,setGlobalRetryPolicy:M,defaultRetry:A,modelGroupRetryPolicy:T,setModelGroupRetryPolicy:I,handleSaveRetrySettings:()=>{ef.mutate({retry_policy:P,model_group_retry_policy:T},{onSuccess:()=>{G.default.success("Retry settings saved successfully"),eR()},onError:()=>{G.default.fromBackend("Failed to save retry settings")}})},isSaving:ef.isPending},"model-retry-settings")},{tab:(0,t.jsx)(e5.Tab,{children:"Model Group Alias"},"model-group-alias"),panel:(0,t.jsx)(J.TabPanel,{children:(0,t.jsx)(t5,{accessToken:u,initialModelGroupAlias:L,onAliasUpdate:O})},"model-group-alias")},{tab:(0,t.jsx)(e5.Tab,{children:"Price Data Reload"},"price-data-reload"),panel:(0,t.jsx)(eF,{},"price-data-reload")}),(0,t.jsxs)(e6.TabGroup,{index:U,onIndexChange:W,className:"gap-2 h-[75vh] w-full ",children:[(0,t.jsxs)(e3.TabList,{className:"flex justify-between mt-2 w-full items-center",children:[(0,t.jsx)("div",{className:"flex",children:i.map(e=>e.tab)}),(0,t.jsxs)("div",{className:"flex items-center space-x-2 self-center",children:[_&&(0,t.jsxs)("span",{className:"text-xs text-gray-500",children:["Last Refreshed: ",_]}),(0,t.jsx)(F.Icon,{icon:e2.RefreshIcon,variant:"shadow",size:"xs",className:"cursor-pointer",onClick:eE})]})]}),(0,t.jsx)(e8.TabPanels,{children:i.map(e=>e.panel)})]}))]})})})};e.s(["default",0,function(){let{premiumUser:e}=(0,r.default)(),{data:l}=(0,u.useTeams)();return(0,t.jsx)(lM,{premiumUser:e,teams:l??null})}],664307)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/039aof7m9ej57.js b/litellm/proxy/_experimental/out/_next/static/chunks/039aof7m9ej57.js deleted file mode 100644 index 364718dd62e..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/039aof7m9ej57.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ArrowLeftOutlined",0,o],447566)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),o=r.default.forwardRef((e,o)=>{let{children:l,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",n)},r.default.createElement("table",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},i),l))});o.displayName="Table",e.s(["Table",0,o],269200)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),o=r.default.forwardRef((e,o)=>{let{children:l,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",n)},i),l))});o.displayName="TableHead",e.s(["TableHead",0,o],427612)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),o=r.default.forwardRef((e,o)=>{let{children:l,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("row"),n)},i),l))});o.displayName="TableRow",e.s(["TableRow",0,o],496020)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),o=r.default.forwardRef((e,o)=>{let{children:l,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",n)},i),l))});o.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,o],64848)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),o=r.default.forwardRef((e,o)=>{let{children:l,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",n)},i),l))});o.displayName="TableBody",e.s(["TableBody",0,o],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),o=r.default.forwardRef((e,o)=>{let{children:l,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:o,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",n)},i),l))});o.displayName="TableCell",e.s(["TableCell",0,o],977572)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},263147,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),s=e.i(431703),o=e.i(708347),l=e.i(135214);let n=(0,r.createQueryKeys)("accessGroups"),i=async e=>{let t=(0,a.getProxyBaseUrl)(),r=`${t}/v1/access_group`,o=await fetch(r,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok){let e=await o.json(),t=(0,s.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return o.json()};e.s(["accessGroupKeys",0,n,"useAccessGroups",0,()=>{let{accessToken:e,userRole:r}=(0,l.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>i(e),enabled:!!e&&o.all_admin_roles.includes(r||"")})}])},304911,e=>{"use strict";var t=e.i(843476),r=e.i(262218);let{Text:a}=e.i(898586).Typography;e.s(["default",0,function({userId:e}){return"default_user_id"===e?(0,t.jsx)(r.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(a,{children:e})}])},384767,e=>{"use strict";var t=e.i(843476),r=e.i(599724),a=e.i(271645),s=e.i(389083);let o=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var l=e.i(602869);let n=function({vectorStores:e,accessToken:n}){let[i,c]=(0,a.useState)([]);return(0,a.useEffect)(()=>{(async()=>{if(n&&0!==e.length)try{let e=await (0,l.vectorStoreListCall)(n);e.data&&c(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[n,e.length]),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,t.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,r)=>{let a;return(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(a=i.find(t=>t.vector_store_id===e))?`${a.vector_store_name||a.vector_store_id} (${a.vector_store_id})`:e},r)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},i=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var c=e.i(871943),d=e.i(502547),m=e.i(592968),u=e.i(234713);let g=function({mcpServers:e,mcpAccessGroups:o=[],mcpToolPermissions:n={},mcpToolsets:g=[],accessToken:h}){let[f,p]=(0,a.useState)([]),[x,v]=(0,a.useState)([]),[b,w]=(0,a.useState)(new Set),[N,y]=(0,a.useState)(new Set);(0,a.useEffect)(()=>{(async()=>{if(h&&e.length>0)try{let e=await (0,l.fetchMCPServers)(h);e&&Array.isArray(e)?p(e):e.data&&Array.isArray(e.data)&&p(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[h,e.length]),(0,a.useEffect)(()=>{(async()=>{if(h&&g.length>0)try{let e=await (0,l.fetchMCPToolsets)(h),t=Array.isArray(e)?e.filter(e=>g.includes(e.toolset_id)):[];v(t)}catch(e){console.error("Error fetching toolsets:",e)}})()},[h,g.length]);let C=e.includes(u.NO_MCP_SERVERS_SENTINEL),j=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),k=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...o.map(e=>({type:"accessGroup",value:e}))],T=k.length+g.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(i,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,t.jsx)(s.Badge,{color:C?"red":"blue",size:"xs",children:C?"Blocked":j?"All":T})]}),C?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsx)(i,{className:"h-4 w-4 text-red-400"}),(0,t.jsx)(r.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):j?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsx)(i,{className:"h-4 w-4 text-blue-400"}),(0,t.jsx)(r.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):T>0?(0,t.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[k.map((e,r)=>{let a="server"===e.type?n[e.value]:void 0,s=a&&a.length>0,o=b.has(e.value);return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>{var t;return s&&(t=e.value,void w(e=>{let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=f.find(t=>t.server_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.alias} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a.length}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===a.length?"tool":"tools"}),o?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&o&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.map((e,r)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},r))})})]},r)}),g.length>0&&g.map((e,r)=>{let a=x.find(t=>t.toolset_id===e),s=N.has(e),o=a?.tools.length??0;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>o>0&&void y(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${o>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:a?.toolset_name??e}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),o>0&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:o}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===o?"tool":"tools"}),s?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),o>0&&s&&a&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.tools.map((e,r)=>(0,t.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,t.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},r))})})]},`toolset-${r}`)})]}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(i,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},h=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),f=function({agents:e,agentAccessGroups:o=[],accessToken:n}){let[i,c]=(0,a.useState)([]);(0,a.useEffect)(()=>{(async()=>{if(n&&e.length>0)try{let e=await (0,l.getAgentsList)(n);e&&e.agents&&Array.isArray(e.agents)&&c(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[n,e.length]);let d=[...e.map(e=>({type:"agent",value:e})),...o.map(e=>({type:"accessGroup",value:e}))],u=d.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(h,{className:"h-4 w-4 text-purple-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,t.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,t.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:d.map((e,r)=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=i.find(t=>t.agent_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.agent_name} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},r))}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(h,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:a="card",className:s="",accessToken:o}){let l=e?.vector_stores||[],i=e?.mcp_servers||[],c=e?.mcp_access_groups||[],d=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],h=e?.agent_access_groups||[],p=e?.search_tools||[],x=(0,t.jsxs)("div",{className:"card"===a?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,t.jsx)(n,{vectorStores:l,accessToken:o}),(0,t.jsx)(g,{mcpServers:i,mcpAccessGroups:c,mcpToolPermissions:d,mcpToolsets:m,accessToken:o}),(0,t.jsx)(f,{agents:u,agentAccessGroups:h,accessToken:o}),(0,t.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,t.jsx)(r.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===p.length?(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-700",children:p.join(", ")})]})]});return"card"===a?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),x]}):(0,t.jsxs)("div",{className:`${s}`,children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),x]})}],384767)},637235,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"}}]},name:"clock-circle",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ClockCircleOutlined",0,o],637235)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["default",0,o],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),s=e.i(271645);let o=s.default.forwardRef((e,o)=>{let{color:l,className:n,children:i}=e;return s.default.createElement("p",{ref:o,className:(0,r.tremorTwMerge)("text-tremor-default",l?(0,a.getColorClassNames)(l,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),n)},i)});o.displayName="Text",e.s(["default",0,o],936325),e.s(["Text",0,o],599724)},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let s=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],o=e=>({_s:e,status:s[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),l=e=>e?6:5,n=(e,t,r,a,s)=>{clearTimeout(a.current);let l=o(e);t(l),r.current=l,s&&s({current:l})};var i=e.i(480731),c=e.i(444755),d=e.i(673706);let m=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var u=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},h=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,d.getColorClassNames)(t,u.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,d.getColorClassNames)(t,u.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,d.getColorClassNames)(t,u.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,d.getColorClassNames)(t,u.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,d.getColorClassNames)(t,u.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,u.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,c.tremorTwMerge)((0,d.getColorClassNames)(t,u.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,d.getColorClassNames)(t,u.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,d.getColorClassNames)(t,u.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,u.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,d.makeClassName)("Button"),p=({loading:e,iconSize:t,iconPosition:r,Icon:s,needMargin:o,transitionStatus:l})=>{let n=o?r===i.HorizontalPositions.Left?(0,c.tremorTwMerge)("-ml-1","mr-1.5"):(0,c.tremorTwMerge)("-mr-1","ml-1.5"):"",d=(0,c.tremorTwMerge)("w-0 h-0"),u={default:d,entering:d,entered:t,exiting:t,exited:d};return e?a.default.createElement(m,{className:(0,c.tremorTwMerge)(f("icon"),"animate-spin shrink-0",n,u.default,u[l]),style:{transition:"width 150ms"}}):a.default.createElement(s,{className:(0,c.tremorTwMerge)(f("icon"),"shrink-0",t,n)})},x=a.default.forwardRef((e,s)=>{let{icon:m,iconPosition:u=i.HorizontalPositions.Left,size:x=i.Sizes.SM,color:v,variant:b="primary",disabled:w,loading:N=!1,loadingText:y,children:C,tooltip:j,className:k}=e,T=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),E=N||w,_=void 0!==m||N,M=N&&y,S=!(!C&&!M),P=(0,c.tremorTwMerge)(g[x].height,g[x].width),R="light"!==b?(0,c.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",L=h(b,v),z=("light"!==b?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[x],{tooltipProps:B,getReferenceProps:$}=(0,r.useTooltip)(300),[O,H]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:s,timeout:i,initialEntered:c,mountOnEnter:d,unmountOnExit:m,onStateChange:u}={})=>{let[g,h]=(0,a.useState)(()=>o(c?2:l(d))),f=(0,a.useRef)(g),p=(0,a.useRef)(0),[x,v]="object"==typeof i?[i.enter,i.exit]:[i,i],b=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return l(t)}})(f.current._s,m);e&&n(e,h,f,p,u)},[u,m]);return[g,(0,a.useCallback)(a=>{let o=e=>{switch(n(e,h,f,p,u),e){case 1:x>=0&&(p.current=((...e)=>setTimeout(...e))(b,x));break;case 4:v>=0&&(p.current=((...e)=>setTimeout(...e))(b,v));break;case 0:case 3:p.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||o(e+1)},0)}},i=f.current.isEnter;"boolean"!=typeof a&&(a=!i),a?i||o(e?+!r:2):i&&o(t?s?3:4:l(m))},[b,u,e,t,r,s,x,v,m]),b]})({timeout:50});return(0,a.useEffect)(()=>{H(N)},[N]),a.default.createElement("button",Object.assign({ref:(0,d.mergeRefs)([s,B.refs.setReference]),className:(0,c.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",R,z.paddingX,z.paddingY,z.fontSize,L.textColor,L.bgColor,L.borderColor,L.hoverBorderColor,E?"opacity-50 cursor-not-allowed":(0,c.tremorTwMerge)(h(b,v).hoverTextColor,h(b,v).hoverBgColor,h(b,v).hoverBorderColor),k),disabled:E},$,T),a.default.createElement(r.default,Object.assign({text:j},B)),_&&u!==i.HorizontalPositions.Right?a.default.createElement(p,{loading:N,iconSize:P,iconPosition:u,Icon:m,transitionStatus:O.status,needMargin:S}):null,M||C?a.default.createElement("span",{className:(0,c.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},M?y:C):null,_&&u===i.HorizontalPositions.Right?a.default.createElement(p,{loading:N,iconSize:P,iconPosition:u,Icon:m,transitionStatus:O.status,needMargin:S}):null)});x.displayName="Button",e.s(["Button",0,x],994388)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),s=e.i(95779),o=e.i(444755),l=e.i(673706);let n=(0,l.makeClassName)("Card"),i=r.default.forwardRef((e,i)=>{let{decoration:c="",decorationColor:d,children:m,className:u}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:i,className:(0,o.tremorTwMerge)(n("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",d?(0,l.getColorClassNames)(d,s.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(c),u)},g),m)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),s=e.i(673706),o=e.i(271645);let l=o.default.forwardRef((e,l)=>{let{color:n,children:i,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return o.default.createElement("p",Object.assign({ref:l,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",n?(0,s.getColorClassNames)(n,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",c)},d),i)});l.displayName="Title",e.s(["Title",0,l],629569)},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},891547,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),s=e.i(602869);e.s(["default",0,({onChange:e,value:o,className:l,accessToken:n,disabled:i})=>{let[c,d]=(0,r.useState)([]),[m,u]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(n){u(!0);try{let e=await (0,s.getGuardrailsList)(n);e.guardrails&&d(e.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{u(!1)}}})()},[n]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:i,placeholder:i?"Setting guardrails is a premium feature.":"Select guardrails",onChange:t=>{e(t)},value:o,loading:m,className:l,allowClear:!0,options:c.map(e=>({label:`${e.guardrail_name}`,value:e.guardrail_name})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})}])},921511,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),s=e.i(602869);function o(e){return e.filter(e=>(e.version_status??"draft")!=="draft").map(e=>{var t;let r=e.version_number??1,a=e.version_status??"draft";return{label:`${e.policy_name} — v${r} (${a})${e.description?` — ${e.description}`:""}`,value:"production"===a?e.policy_name:e.policy_id?(t=e.policy_id,`policy_${t}`):e.policy_name}})}e.s(["default",0,({onChange:e,value:l,className:n,accessToken:i,disabled:c,onPoliciesLoaded:d})=>{let[m,u]=(0,r.useState)([]),[g,h]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(i){h(!0);try{let e=await (0,s.getPoliciesList)(i);e.policies&&(u(e.policies),d?.(e.policies))}catch(e){console.error("Error fetching policies:",e)}finally{h(!1)}}})()},[i,d]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:c,placeholder:c?"Setting policies is a premium feature.":"Select policies (production or published versions)",onChange:t=>{e(t)},value:l,loading:g,className:n,allowClear:!0,options:o(m),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})},"getPolicyOptionEntries",0,o])},530212,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,r],530212)},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},555987,e=>{"use strict";var t=e.i(221688),r=e.i(950643);let a=/^(https?:|data:|blob:|\/\/)/i;e.s(["resolveLogoSrc",0,(e,s=t.serverRootPath)=>{if(e){let t;return a.test(e)?e:(t=(0,r.normalizeRootPath)(s),`${t}${e.startsWith("/")?e:`/${e}`}`)}}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let o=e<0?"-":"",l=Math.abs(e),n=l,i="";return l>=1e6?(n=l/1e6,i="M"):l>=1e3&&(n=l/1e3,i="K"),`${o}${n.toLocaleString("en-US",s)}${i}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(264042).Row;e.s(["Row",0,t],621192)},962944,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"}}]},name:"thunderbolt",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ThunderboltOutlined",0,o],962944)},72713,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"}}]},name:"calendar",theme:"outlined"};var s=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(s.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["CalendarOutlined",0,o],72713)},439189,435684,96226,497245,e=>{"use strict";function t(e){let t=Object.prototype.toString.call(e);return e instanceof Date||"object"==typeof e&&"[object Date]"===t?new e.constructor(+e):new Date("number"==typeof e||"[object Number]"===t||"string"==typeof e||"[object String]"===t?e:NaN)}function r(e,t){return e instanceof Date?new e.constructor(t):new Date(t)}e.s(["toDate",0,t],435684),e.s(["constructFrom",0,r],96226),e.s(["addDays",0,function(e,a){let s=t(e);return isNaN(a)?r(e,NaN):(a&&s.setDate(s.getDate()+a),s)}],439189),e.s(["addMonths",0,function(e,a){let s=t(e);if(isNaN(a))return r(e,NaN);if(!a)return s;let o=s.getDate(),l=r(e,s.getTime());return(l.setMonth(s.getMonth()+a+1,0),o>=l.getDate())?l:(s.setFullYear(l.getFullYear(),l.getMonth(),o),s)}],497245)},24529,e=>{"use strict";var t=e.i(439189),r=e.i(497245),a=e.i(96226),s=e.i(435684);function o(e,o){let{years:l=0,months:n=0,weeks:i=0,days:c=0,hours:d=0,minutes:m=0,seconds:u=0}=o,g=(0,s.toDate)(e),h=n||l?(0,r.addMonths)(g,n+12*l):g,f=c||i?(0,t.addDays)(h,c+7*i):h;return(0,a.constructFrom)(e,f.getTime()+1e3*(u+60*(m+60*d)))}let l=/[zZ]$|[+-]\d{2}:?\d{2}$/;function n(e){return Date.parse(l.test(e)?e:`${e}Z`)}e.s(["calculateExpiryPreviewFromDuration",0,function(e){if(!e)return null;try{let t,r=parseInt(e);if(Number.isNaN(r))throw Error("Invalid duration format");let a=new Date;if(e.endsWith("mo"))t=o(a,{months:r});else if(e.endsWith("s"))t=o(a,{seconds:r});else if(e.endsWith("m"))t=o(a,{minutes:r});else if(e.endsWith("h"))t=o(a,{hours:r});else if(e.endsWith("d"))t=o(a,{days:r});else if(e.endsWith("w"))t=o(a,{weeks:r});else throw Error("Invalid duration format");return t.toLocaleString()}catch{return null}},"formatExpiresUtc",0,function(e){let t=n(e);return Number.isNaN(t)?e:new Date(t).toLocaleString()},"isKeyExpired",0,function(e){if(!e)return!1;let t=n(e);return!Number.isNaN(t)&&t{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},94629,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,r],94629)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(829087),a=e.i(480731),s=e.i(444755),n=e.i(673706),i=e.i(95779);let l={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,n.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:h="simple",tooltip:b,size:f=a.Sizes.SM,color:p,className:C}=e,w=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),k=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,n.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,n.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,n.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,n.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,n.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,n.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,s.tremorTwMerge)((0,n.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,n.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,n.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,n.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,s.tremorTwMerge)((0,n.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(h,p),{tooltipProps:x,getReferenceProps:v}=(0,o.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,n.mergeRefs)([m,x.refs.setReference]),className:(0,s.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",k.bgColor,k.textColor,k.borderColor,k.ringColor,c[h].rounded,c[h].border,c[h].shadow,c[h].ring,l[f].paddingX,l[f].paddingY,C)},v,w),r.default.createElement(o.default,Object.assign({text:b},x)),r.default.createElement(g,{className:(0,s.tremorTwMerge)(u("icon"),"shrink-0",d[f].height,d[f].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],s=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,i=(e,t,r,o,a)=>{clearTimeout(o.current);let n=s(e);t(n),r.current=n,a&&a({current:n})};var l=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},h=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},b=(0,c.makeClassName)("Button"),f=({loading:e,iconSize:t,iconPosition:r,Icon:a,needMargin:s,transitionStatus:n})=>{let i=s?r===l.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?o.default.createElement(u,{className:(0,d.tremorTwMerge)(b("icon"),"animate-spin shrink-0",i,m.default,m[n]),style:{transition:"width 150ms"}}):o.default.createElement(a,{className:(0,d.tremorTwMerge)(b("icon"),"shrink-0",t,i)})},p=o.default.forwardRef((e,a)=>{let{icon:u,iconPosition:m=l.HorizontalPositions.Left,size:p=l.Sizes.SM,color:C,variant:w="primary",disabled:k,loading:x=!1,loadingText:v,children:N,tooltip:y,className:M}=e,T=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),E=x||k,R=void 0!==u||x,P=x&&v,O=!(!N&&!P),j=(0,d.tremorTwMerge)(g[p].height,g[p].width),S="light"!==w?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",z=h(w,C),L=("light"!==w?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[p],{tooltipProps:B,getReferenceProps:_}=(0,r.useTooltip)(300),[H,X]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:a,timeout:l,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,h]=(0,o.useState)(()=>s(d?2:n(c))),b=(0,o.useRef)(g),f=(0,o.useRef)(0),[p,C]="object"==typeof l?[l.enter,l.exit]:[l,l],w=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(b.current._s,u);e&&i(e,h,b,f,m)},[m,u]);return[g,(0,o.useCallback)(o=>{let s=e=>{switch(i(e,h,b,f,m),e){case 1:p>=0&&(f.current=((...e)=>setTimeout(...e))(w,p));break;case 4:C>=0&&(f.current=((...e)=>setTimeout(...e))(w,C));break;case 0:case 3:f.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||s(e+1)},0)}},l=b.current.isEnter;"boolean"!=typeof o&&(o=!l),o?l||s(e?+!r:2):l&&s(t?a?3:4:n(u))},[w,m,e,t,r,a,p,C,u]),w]})({timeout:50});return(0,o.useEffect)(()=>{X(x)},[x]),o.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([a,B.refs.setReference]),className:(0,d.tremorTwMerge)(b("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",S,L.paddingX,L.paddingY,L.fontSize,z.textColor,z.bgColor,z.borderColor,z.hoverBorderColor,E?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(h(w,C).hoverTextColor,h(w,C).hoverBgColor,h(w,C).hoverBorderColor),M),disabled:E},_,T),o.default.createElement(r.default,Object.assign({text:y},B)),R&&m!==l.HorizontalPositions.Right?o.default.createElement(f,{loading:x,iconSize:j,iconPosition:m,Icon:u,transitionStatus:H.status,needMargin:O}):null,P||N?o.default.createElement("span",{className:(0,d.tremorTwMerge)(b("text"),"text-tremor-default whitespace-nowrap")},P?v:N):null,R&&m===l.HorizontalPositions.Right?o.default.createElement(f,{loading:x,iconSize:j,iconPosition:m,Icon:u,transitionStatus:H.status,needMargin:O}):null)});p.displayName="Button",e.s(["Button",0,p],994388)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),a=e.i(95779),s=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Card"),l=r.default.forwardRef((e,l)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:l,className:(0,s.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,n.getColorClassNames)(c,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});l.displayName="Card",e.s(["Card",0,l],304967)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("Table"),s=r.default.forwardRef((e,s)=>{let{children:n,className:i}=e,l=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,o.tremorTwMerge)(a("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:s,className:(0,o.tremorTwMerge)(a("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},l),n))});s.displayName="Table",e.s(["Table",0,s],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableBody"),s=r.default.forwardRef((e,s)=>{let{children:n,className:i}=e,l=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:s,className:(0,o.tremorTwMerge)(a("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},l),n))});s.displayName="TableBody",e.s(["TableBody",0,s],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableCell"),s=r.default.forwardRef((e,s)=>{let{children:n,className:i}=e,l=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:s,className:(0,o.tremorTwMerge)(a("root"),"align-middle whitespace-nowrap text-left p-4",i)},l),n))});s.displayName="TableCell",e.s(["TableCell",0,s],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableHead"),s=r.default.forwardRef((e,s)=>{let{children:n,className:i}=e,l=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:s,className:(0,o.tremorTwMerge)(a("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},l),n))});s.displayName="TableHead",e.s(["TableHead",0,s],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableHeaderCell"),s=r.default.forwardRef((e,s)=>{let{children:n,className:i}=e,l=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:s,className:(0,o.tremorTwMerge)(a("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},l),n))});s.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,s],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableRow"),s=r.default.forwardRef((e,s)=>{let{children:n,className:i}=e,l=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:s,className:(0,o.tremorTwMerge)(a("row"),i)},l),n))});s.displayName="TableRow",e.s(["TableRow",0,s],496020)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},530212,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,r],530212)},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},988297,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,r],988297)},797672,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,r],797672)},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),o=e.i(540143),a=e.i(915823),s=e.i(619273),n=class extends a.Subscribable{#e;#t=void 0;#r;#o;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#a()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,s.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,s.hashKey)(t.mutationKey)!==(0,s.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#a(),this.#s(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#a(),this.#s()}mutate(e,t){return this.#o=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#a(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#s(e){o.notifyManager.batch(()=>{if(this.#o&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,o={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#o.onSuccess?.(e.data,t,r,o)}catch(e){Promise.reject(e)}try{this.#o.onSettled?.(e.data,null,t,r,o)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#o.onError?.(e.error,t,r,o)}catch(e){Promise.reject(e)}try{this.#o.onSettled?.(void 0,e.error,t,r,o)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},i=e.i(912598);e.s(["useMutation",0,function(e,r){let a=(0,i.useQueryClient)(r),[l]=t.useState(()=>new n(a,e));t.useEffect(()=>{l.setOptions(e)},[l,e]);let d=t.useSyncExternalStore(t.useCallback(e=>l.subscribe(o.notifyManager.batchCalls(e)),[l]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),c=t.useCallback((e,t)=>{l.mutate(e,t).catch(s.noop)},[l]);if(d.error&&(0,s.shouldThrowError)(l.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},220508,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["CheckCircleIcon",0,r],220508)},888288,e=>{"use strict";var t=e.i(271645);e.s(["default",0,(e,r)=>{let o=void 0!==r,[a,s]=(0,t.useState)(e);return[o?r:a,e=>{o||s(e)}]}])},240647,e=>{"use strict";var t=e.i(286612);e.s(["RightOutlined",()=>t.default])},245704,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"check-circle",theme:"outlined"};var a=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(a.default,(0,t.default)({},e,{ref:s,icon:o}))});e.s(["CheckCircleOutlined",0,s],245704)},518617,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{"fill-rule":"evenodd",viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm0 76c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm128.01 198.83c.03 0 .05.01.09.06l45.02 45.01a.2.2 0 01.05.09.12.12 0 010 .07c0 .02-.01.04-.05.08L557.25 512l127.87 127.86a.27.27 0 01.05.06v.02a.12.12 0 010 .07c0 .03-.01.05-.05.09l-45.02 45.02a.2.2 0 01-.09.05.12.12 0 01-.07 0c-.02 0-.04-.01-.08-.05L512 557.25 384.14 685.12c-.04.04-.06.05-.08.05a.12.12 0 01-.07 0c-.03 0-.05-.01-.09-.05l-45.02-45.02a.2.2 0 01-.05-.09.12.12 0 010-.07c0-.02.01-.04.06-.08L466.75 512 338.88 384.14a.27.27 0 01-.05-.06l-.01-.02a.12.12 0 010-.07c0-.03.01-.05.05-.09l45.02-45.02a.2.2 0 01.09-.05.12.12 0 01.07 0c.02 0 .04.01.08.06L512 466.75l127.86-127.86c.04-.05.06-.06.08-.06a.12.12 0 01.07 0z"}}]},name:"close-circle",theme:"outlined"};var a=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(a.default,(0,t.default)({},e,{ref:s,icon:o}))});e.s(["CloseCircleOutlined",0,s],518617)},848725,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"}))});e.s(["EyeIcon",0,r],848725)},78085,e=>{"use strict";var t=e.i(290571),r=e.i(103471),o=e.i(888288),a=e.i(271645),s=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Textarea"),l=a.default.forwardRef((e,l)=>{let{value:d,defaultValue:c="",placeholder:u="Type...",error:m=!1,errorMessage:g,disabled:h=!1,className:b,onChange:f,onValueChange:p,autoHeight:C=!1}=e,w=(0,t.__rest)(e,["value","defaultValue","placeholder","error","errorMessage","disabled","className","onChange","onValueChange","autoHeight"]),[k,x]=(0,o.default)(c,d),v=(0,a.useRef)(null),N=(0,r.hasValue)(k);return(0,a.useEffect)(()=>{let e=v.current;if(C&&e){e.style.height="60px";let t=e.scrollHeight;e.style.height=t+"px"}},[C,v,k]),a.default.createElement(a.default.Fragment,null,a.default.createElement("textarea",Object.assign({ref:(0,n.mergeRefs)([v,l]),value:k,placeholder:u,disabled:h,className:(0,s.tremorTwMerge)(i("Textarea"),"w-full flex items-center outline-none rounded-tremor-default px-3 py-2 text-tremor-default focus:ring-2 transition duration-100 border","shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:shadow-dark-tremor-input focus:dark:border-dark-tremor-brand-subtle focus:dark:ring-dark-tremor-brand-muted",(0,r.getSelectButtonColors)(N,h,m),h?"placeholder:text-tremor-content-subtle dark:placeholder:text-dark-tremor-content-subtle":"placeholder:text-tremor-content dark:placeholder:text-dark-tremor-content",b),"data-testid":"text-area",onChange:e=>{null==f||f(e),x(e.target.value),null==p||p(e.target.value)}},w)),m&&g?a.default.createElement("p",{className:(0,s.tremorTwMerge)(i("errorMessage"),"text-sm text-red-500 mt-1")},g):null)});l.displayName="Textarea",e.s(["Textarea",0,l],78085)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03e_5nw.1urn4.js b/litellm/proxy/_experimental/out/_next/static/chunks/03e_5nw.1urn4.js new file mode 100644 index 00000000000..d6539931fe4 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03e_5nw.1urn4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,l],250980)},309426,e=>{"use strict";var t=e.i(290571),l=e.i(444755),r=e.i(673706),s=e.i(271645),a=e.i(46757);let n=(0,r.makeClassName)("Col"),i=s.default.forwardRef((e,r)=>{let i,o,c,d,{numColSpan:u=1,numColSpanSm:m,numColSpanMd:p,numColSpanLg:g,children:f,className:h}=e,x=(0,t.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),v=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"";return s.default.createElement("div",Object.assign({ref:r,className:(0,l.tremorTwMerge)(n("root"),(i=v(u,a.colSpan),o=v(m,a.colSpanSm),c=v(p,a.colSpanMd),d=v(g,a.colSpanLg),(0,l.tremorTwMerge)(i,o,c,d)),h)},x),f)});i.displayName="Col",e.s(["Col",0,i],309426)},950724,(e,t,l)=>{t.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},100236,(e,t,l)=>{t.exports=e.g&&e.g.Object===Object&&e.g},139088,(e,t,l)=>{var r=e.r(100236),s="object"==typeof self&&self&&self.Object===Object&&self;t.exports=r||s||Function("return this")()},631926,(e,t,l)=>{var r=e.r(139088);t.exports=function(){return r.Date.now()}},748891,(e,t,l)=>{var r=/\s/;t.exports=function(e){for(var t=e.length;t--&&r.test(e.charAt(t)););return t}},830364,(e,t,l)=>{var r=e.r(748891),s=/^\s+/;t.exports=function(e){return e?e.slice(0,r(e)+1).replace(s,""):e}},630353,(e,t,l)=>{t.exports=e.r(139088).Symbol},243436,(e,t,l)=>{var r=e.r(630353),s=Object.prototype,a=s.hasOwnProperty,n=s.toString,i=r?r.toStringTag:void 0;t.exports=function(e){var t=a.call(e,i),l=e[i];try{e[i]=void 0;var r=!0}catch(e){}var s=n.call(e);return r&&(t?e[i]=l:delete e[i]),s}},223243,(e,t,l)=>{var r=Object.prototype.toString;t.exports=function(e){return r.call(e)}},377684,(e,t,l)=>{var r=e.r(630353),s=e.r(243436),a=e.r(223243),n=r?r.toStringTag:void 0;t.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":n&&n in Object(e)?s(e):a(e)}},877289,(e,t,l)=>{t.exports=function(e){return null!=e&&"object"==typeof e}},361884,(e,t,l)=>{var r=e.r(377684),s=e.r(877289);t.exports=function(e){return"symbol"==typeof e||s(e)&&"[object Symbol]"==r(e)}},773759,(e,t,l)=>{var r=e.r(830364),s=e.r(950724),a=e.r(361884),n=0/0,i=/^[-+]0x[0-9a-f]+$/i,o=/^0b[01]+$/i,c=/^0o[0-7]+$/i,d=parseInt;t.exports=function(e){if("number"==typeof e)return e;if(a(e))return n;if(s(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=s(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var l=o.test(e);return l||c.test(e)?d(e.slice(2),l?2:8):i.test(e)?n:+e}},374009,(e,t,l)=>{var r=e.r(950724),s=e.r(631926),a=e.r(773759),n=Math.max,i=Math.min;t.exports=function(e,t,l){var o,c,d,u,m,p,g=0,f=!1,h=!1,x=!0;if("function"!=typeof e)throw TypeError("Expected a function");function v(t){var l=o,r=c;return o=c=void 0,g=t,u=e.apply(r,l)}function y(e){var l=e-p,r=e-g;return void 0===p||l>=t||l<0||h&&r>=d}function b(){var e,l,r,a=s();if(y(a))return w(a);m=setTimeout(b,(e=a-p,l=a-g,r=t-e,h?i(r,d-l):r))}function w(e){return(m=void 0,x&&o)?v(e):(o=c=void 0,u)}function j(){var e,l=s(),r=y(l);if(o=arguments,c=this,p=l,r){if(void 0===m)return g=e=p,m=setTimeout(b,t),f?v(e):u;if(h)return clearTimeout(m),m=setTimeout(b,t),v(p)}return void 0===m&&(m=setTimeout(b,t)),u}return t=a(t)||0,r(l)&&(f=!!l.leading,d=(h="maxWait"in l)?n(a(l.maxWait)||0,t):d,x="trailing"in l?!!l.trailing:x),j.cancel=function(){void 0!==m&&clearTimeout(m),g=0,o=p=c=m=void 0},j.flush=function(){return void 0===m?u:w(s())},j}},435451,e=>{"use strict";var t=e.i(843476),l=e.i(290571),r=e.i(271645);let s=e=>{var t=(0,l.__rest)(e,[]);return r.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),r.default.createElement("path",{d:"M12 4v16m8-8H4"}))},a=e=>{var t=(0,l.__rest)(e,[]);return r.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),r.default.createElement("path",{d:"M20 12H4"}))};var n=e.i(444755),i=e.i(673706),o=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",d="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",u=r.default.forwardRef((e,t)=>{let{onSubmit:u,enableStepper:m=!0,disabled:p,onValueChange:g,onChange:f}=e,h=(0,l.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),x=(0,r.useRef)(null),[v,y]=r.default.useState(!1),b=r.default.useCallback(()=>{y(!0)},[]),w=r.default.useCallback(()=>{y(!1)},[]),[j,N]=r.default.useState(!1),S=r.default.useCallback(()=>{N(!0)},[]),k=r.default.useCallback(()=>{N(!1)},[]);return r.default.createElement(o.default,Object.assign({type:"number",ref:(0,i.mergeRefs)([x,t]),disabled:p,makeInputClassName:(0,i.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=x.current)?void 0:t.value;null==u||u(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&b(),"ArrowUp"===e.key&&S()},onKeyUp:e=>{"ArrowDown"===e.key&&w(),"ArrowUp"===e.key&&k()},onChange:e=>{p||(null==g||g(parseFloat(e.target.value)),null==f||f(e))},stepper:m?r.default.createElement("div",{className:(0,n.tremorTwMerge)("flex justify-center align-middle")},r.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null==(e=x.current)||e.stepDown(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!p&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},r.default.createElement(a,{"data-testid":"step-down",className:(v?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),r.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null==(e=x.current)||e.stepUp(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!p&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},r.default.createElement(s,{"data-testid":"step-up",className:(j?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},h))});u.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:l={width:"100%"},placeholder:r="Enter a numerical value",min:s,max:a,onChange:n,...i})=>(0,t.jsx)(u,{onWheel:e=>e.currentTarget.blur(),step:e,style:l,placeholder:r,min:s,max:a,onChange:n,...i})],435451)},355619,e=>{"use strict";var t=e.i(602869);let l=async(e,l,r)=>{try{if(null===e||null===l)return;if(null!==r){let s=(await (0,t.modelAvailableCall)(r,e,l,!0,null,!0)).data.map(e=>e.id),a=[],n=[];return s.forEach(e=>{e.endsWith("/*")?a.push(e):n.push(e)}),[...a,...n]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["excludeProxyWideSentinel",0,e=>e.filter(e=>"all-proxy-models"!==e),"fetchAvailableModelsForTeamOrKey",0,l,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let t=e.replace("/*","");return`All ${t} models`}return e},"hasAllModelsSentinel",0,e=>e.includes("all-proxy-models")||e.includes("all-team-models"),"unfurlWildcardModelsInList",0,(e,t)=>{let l=[],r=[];return e.forEach(e=>{if(e.endsWith("/*")){let s=e.replace("/*",""),a=t.filter(e=>e.startsWith(s+"/"));r.push(...a),l.push(e)}else r.push(e)}),[...l,...r].filter((e,t,l)=>l.indexOf(e)===t)}])},860585,e=>{"use strict";var t=e.i(843476),l=e.i(199133);let{Option:r}=l.Select;e.s(["default",0,({value:e,onChange:s,className:a="",style:n={}})=>(0,t.jsxs)(l.Select,{style:{width:"100%",...n},value:e||void 0,onChange:s,className:a,placeholder:"n/a",allowClear:!0,children:[(0,t.jsx)(r,{value:"1h",children:"hourly"}),(0,t.jsx)(r,{value:"24h",children:"daily"}),(0,t.jsx)(r,{value:"7d",children:"weekly"}),(0,t.jsx)(r,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},213205,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var s=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(s.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["UserAddOutlined",0,a],213205)},350967,46757,e=>{"use strict";var t=e.i(290571),l=e.i(444755),r=e.i(673706),s=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},n={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},i={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},o={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,o,"gridColsMd",0,i,"gridColsSm",0,n],46757);let c=(0,r.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=s.default.forwardRef((e,r)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:p,numItemsLg:g,children:f,className:h}=e,x=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),v=d(u,a),y=d(m,n),b=d(p,i),w=d(g,o),j=(0,l.tremorTwMerge)(v,y,b,w);return s.default.createElement("div",Object.assign({ref:r,className:(0,l.tremorTwMerge)(c("root"),"grid",j,h)},x),f)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),l=e.i(243652),r=e.i(602869),s=e.i(135214);let a=(0,l.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:l}=(0,s.default)();return(0,t.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,r.fetchMCPServers)(l,e),enabled:!!l})}])},699857,e=>{"use strict";var t=e.i(266027),l=e.i(243652),r=e.i(602869),s=e.i(135214);let a=(0,l.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,s.default)();return(0,t.useQuery)({queryKey:a.list(),queryFn:async()=>await (0,r.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(199133),s=e.i(602869);e.s(["default",0,({onChange:e,value:a,className:n,accessToken:i,placeholder:o="Select vector stores",disabled:c=!1})=>{let[d,u]=(0,l.useState)([]),[m,p]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{(async()=>{if(i){p(!0);try{let e=await (0,s.vectorStoreListCall)(i);e.data&&u(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{p(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(r.Select,{mode:"multiple",placeholder:o,onChange:e,value:a,loading:m,className:n,allowClear:!0,options:d.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:c})})}])},75921,e=>{"use strict";var t=e.i(843476),l=e.i(266027),r=e.i(243652),s=e.i(602869),a=e.i(135214);let n=(0,r.createQueryKeys)("mcpAccessGroups");var i=e.i(500727),o=e.i(699857),c=e.i(199133),d=e.i(234713);let u="toolset:";e.s(["default",0,({onChange:e,value:r,className:m,accessToken:p,placeholder:g="Select MCP servers",disabled:f=!1,teamId:h,allowNoMcpServers:x=!1,allowAllProxyMcpServers:v=!1})=>{let{data:y=[],isLoading:b}=(0,i.useMCPServers)(h),{data:w=[],isLoading:j}=(()=>{let{accessToken:e}=(0,a.default)();return(0,l.useQuery)({queryKey:n.list({}),queryFn:async()=>await (0,s.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:N=[],isLoading:S}=(0,o.useMCPToolsets)(),k=new Set(w),C=[...w.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...y.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...N.map(e=>({label:e.toolset_name,value:`${u}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],_={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...r?.servers||[],...r?.accessGroups||[],...(r?.toolsets||[]).map(e=>`${u}${e}`)],T=x&&E.includes(d.NO_MCP_SERVERS_SENTINEL),L=E.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,t.jsx)("div",{children:(0,t.jsxs)(c.Select,{mode:"multiple",placeholder:g,onChange:t=>{if(v&&t.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[d.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(x&&t.includes(d.NO_MCP_SERVERS_SENTINEL))return void e({servers:[d.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let l=t.filter(e=>e.startsWith(u)).map(e=>e.slice(u.length)),r=t.filter(e=>!e.startsWith(u));e({servers:r.filter(e=>!k.has(e)),accessGroups:r.filter(e=>k.has(e)),toolsets:l})},value:E,loading:b||j||S,className:m,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:f,filterOption:(e,t)=>t?.value===d.NO_MCP_SERVERS_SENTINEL||t?.value===d.ALL_PROXY_MCP_SERVERS_SENTINEL||(C.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(v||L)&&(0,t.jsx)(c.Select.Option,{value:d.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,t.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},d.ALL_PROXY_MCP_SERVERS_SENTINEL),x&&(0,t.jsx)(c.Select.Option,{value:d.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,t.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},d.NO_MCP_SERVERS_SENTINEL),C.map(e=>(0,t.jsx)(c.Select.Option,{value:e.value,label:e.label,disabled:T||L,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:_[e.type],flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:_[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var s=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(s.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["RobotOutlined",0,a],983561)},797672,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,l],797672)},992619,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(779241),s=e.i(599724),a=e.i(199133),n=e.i(983561),i=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:p,showLabel:g=!0,labelText:f="Select Model"})=>{let[h,x]=(0,l.useState)(o),[v,y]=(0,l.useState)(!1),[b,w]=(0,l.useState)([]),j=(0,l.useRef)(null);return(0,l.useEffect)(()=>{x(o)},[o]),(0,l.useEffect)(()=>{e&&(async()=>{try{let t=await (0,i.fetchAvailableModels)(e);t.length>0&&w(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[g&&(0,t.jsxs)(s.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(n.RobotOutlined,{className:"mr-2"})," ",f]}),(0,t.jsx)(a.Select,{value:h,placeholder:c,onChange:e=>{"custom"===e?(y(!0),x(void 0)):(y(!1),x(e),d&&d(e))},options:[...Array.from(new Set(b.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${p||""}`,disabled:u}),v&&(0,t.jsx)(r.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{j.current&&clearTimeout(j.current),j.current=setTimeout(()=>{x(e),d&&d(e)},500)},disabled:u})]})}])},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},988297,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,l],988297)},531516,696609,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(536916),s=e.i(599724),a=e.i(409797),n=e.i(246349),n=n;let i=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let l=e.toLowerCase();if(d.test(l))return"read";if(i.test(l))return"delete";if(c.test(l))return"update";if(o.test(l))return"create";if(t){let e=t.toLowerCase();if(d.test(e))return"read";if(i.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let l of e)t[u(l.name,l.description)].push(l);return t}let p={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,p,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let g=["read","create","update","delete","unknown"],f={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},h={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},x={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:i,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[u,v]=(0,l.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),y=(0,l.useMemo)(()=>m(e),[e]),b=(0,l.useMemo)(()=>new Set(void 0===i?e.map(e=>e.name):i),[i,e]),w=e=>{if(c)return;let t=new Set(b);t.has(e)?t.delete(e):t.add(e),o(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:g.map(e=>{let l,i=y[e];if(0===i.length)return null;if(d){let e=d.toLowerCase();if(!i.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=p[e],g=(l=y[e]).length>0&&l.every(e=>b.has(e.name)),j=(e=>{let t=y[e];if(0===t.length)return!1;let l=t.filter(e=>b.has(e.name)).length;return l>0&&l{v(t=>({...t,[e]:!t[e]}))},children:[N?(0,t.jsx)(n.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(a.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${f[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[i.filter(e=>b.has(e.name)).length,"/",i.length," allowed"]})]}),!c&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(s.Text,{className:"text-xs text-gray-500",children:g?"All on":j?"Partial":"All off"}),(0,t.jsx)(r.Checkbox,{checked:g,indeterminate:j,onChange:t=>((e,t)=>{if(c)return;let l=new Set(b);for(let r of y[e])t?l.add(r.name):l.delete(r.name);o(Array.from(l))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!N&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!N&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:i.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let l,a=(l=e.name,b.has(l));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${a?"":"opacity-60"}`,onClick:()=>w(e.name),children:[(0,t.jsx)(r.Checkbox,{checked:a,onChange:()=>w(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(s.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(s.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${a?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:a?"on":"off"})]},e.name)})})]},e)})})}],531516)},37727,e=>{"use strict";var t=e.i(841947);e.s(["X",()=>t.default])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},107233,e=>{"use strict";var t=e.i(603908);e.s(["Plus",()=>t.default])},425063,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,t],425063)},158392,63209,e=>{"use strict";var t=e.i(843476),l=e.i(311451);let r={ttl:3600,lowest_latency_buffer:0},s=({routingStrategyArgs:e})=>{let s={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||r).map(([e,r])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s[e]||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:"object"==typeof r?JSON.stringify(r,null,2):r?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"})]})},a=({routerSettings:e,routerFieldsMetadata:r})=>(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,s])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r[e]?.ui_field_name||e}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r[e]?.field_description||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:null==s||"null"===s?"":"object"==typeof s?JSON.stringify(s,null,2):s?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var n=e.i(199133);let i=({selectedStrategy:e,availableStrategies:l,routingStrategyDescriptions:r,routerFieldsMetadata:s,onStrategyChange:a})=>(0,t.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:s.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s.routing_strategy?.field_description||""})]}),(0,t.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,t.jsx)(n.Select,{value:e,onChange:a,style:{width:"100%"},size:"large",children:l.map(e=>(0,t.jsx)(n.Select.Option,{value:e,label:e,children:(0,t.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,t.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),r[e]&&(0,t.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:r[e]})]})},e))})})]});var o=e.i(790848);let c=({enabled:e,routerFieldsMetadata:l,onToggle:r})=>(0,t.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:l.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,t.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[l.enable_tag_filtering?.field_description||"",l.enable_tag_filtering?.link&&(0,t.jsxs)(t.Fragment,{children:[" ",(0,t.jsx)("a",{href:l.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,t.jsx)(o.Switch,{checked:e,onChange:r,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:l,routerFieldsMetadata:r,availableRoutingStrategies:n,routingStrategyDescriptions:o})=>(0,t.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),n.length>0&&(0,t.jsx)(i,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:n,routingStrategyDescriptions:o,routerFieldsMetadata:r,onStrategyChange:t=>{l({...e,selectedStrategy:t})}}),(0,t.jsx)(c,{enabled:e.enableTagFiltering,routerFieldsMetadata:r,onToggle:t=>{l({...e,enableTagFiltering:t})}})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,t.jsx)(s,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,t.jsx)(a,{routerSettings:e.routerSettings,routerFieldsMetadata:r})]})],158392);var d=e.i(361653);e.s(["AlertCircle",()=>d.default],63209)},419470,e=>{"use strict";var t=e.i(843476),l=e.i(994388),r=e.i(653496),s=e.i(107233),a=e.i(271645),n=e.i(888259),i=e.i(199133),o=e.i(592968),c=e.i(63209),d=e.i(425063),u=e.i(37727);function m({group:e,onChange:l,availableModels:r,maxFallbacks:s}){let a=r.filter(t=>t!==e.primaryModel),n=e.fallbackModels.length{let r=[...e.fallbackModels];r.includes(t)&&(r=r.filter(e=>e!==t)),l({...e,primaryModel:t,fallbackModels:r})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:r.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,t.jsx)(c.AlertCircle,{className:"w-4 h-4"}),(0,t.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,t.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,t.jsx)(d.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,t.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,t.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,t.jsx)("span",{className:"text-red-500",children:"*"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",s," fallbacks at a time)"]})]}),(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(i.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:n?"Select fallback models to add...":`Maximum ${s} fallbacks reached`,value:e.fallbackModels,onChange:t=>{let r=t.slice(0,s);l({...e,fallbackModels:r})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:a.map(e=>({label:e,value:e})),optionRender:(l,r)=>{let s=e.fallbackModels.includes(l.value),a=s?e.fallbackModels.indexOf(l.value)+1:null;return(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[s&&null!==a&&(0,t.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:a}),(0,t.jsx)("span",{children:l.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:n?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${s} used)`:`Maximum ${s} fallbacks reached. Remove some to add more.`})]}),(0,t.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,t.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,t.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,t.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((r,s)=>(0,t.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,t.jsx)("span",{className:"text-xs font-bold",children:s+1})}),(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"font-medium text-gray-800",children:r})})]}),(0,t.jsx)("button",{type:"button",onClick:()=>{let t;return t=e.fallbackModels.filter((e,t)=>t!==s),void l({...e,fallbackModels:t})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,t.jsx)(u.X,{className:"w-4 h-4"})})]},`${r}-${s}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:i,availableModels:o,maxFallbacks:c=10,maxGroups:d=5}){let[u,p]=(0,a.useState)(e.length>0?e[0].id:"1");(0,a.useEffect)(()=>{e.length>0?e.some(e=>e.id===u)||p(e[0].id):p("1")},[e]);let g=()=>{if(e.length>=d)return;let t=Date.now().toString();i([...e,{id:t,primaryModel:null,fallbackModels:[]}]),p(t)},f=t=>{i(e.map(e=>e.id===t.id?t:e))},h=e.map((l,r)=>{let s=l.primaryModel?l.primaryModel:`Group ${r+1}`;return{key:l.id,label:s,closable:e.length>1,children:(0,t.jsx)(m,{group:l,onChange:f,availableModels:o,maxFallbacks:c})}});return 0===e.length?(0,t.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,t.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,t.jsx)(l.Button,{variant:"primary",onClick:g,icon:()=>(0,t.jsx)(s.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,t.jsx)(r.Tabs,{type:"editable-card",activeKey:u,onChange:p,onEdit:(t,l)=>{"add"===l?g():"remove"===l&&e.length>1&&(t=>{if(1===e.length)return n.default.warning("At least one group is required");let l=e.filter(e=>e.id!==t);i(l),u===t&&l.length>0&&p(l[l.length-1].id)})(t)},items:h,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=d})}],419470)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0bj0yt1e7b-fn.js b/litellm/proxy/_experimental/out/_next/static/chunks/03m16pvgn6tls.js similarity index 89% rename from litellm/proxy/_experimental/out/_next/static/chunks/0bj0yt1e7b-fn.js rename to litellm/proxy/_experimental/out/_next/static/chunks/03m16pvgn6tls.js index 9e9cd1aeb5e..604bf07d6dd 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0bj0yt1e7b-fn.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03m16pvgn6tls.js @@ -1 +1 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,298805,e=>{"use strict";var t=e.i(843476),s=e.i(271645),a=e.i(994388),l=e.i(304967),i=e.i(269200),r=e.i(942232),n=e.i(977572),o=e.i(427612),c=e.i(64848),d=e.i(496020),m=e.i(389083),p=e.i(599724),u=e.i(212931),h=e.i(560445),x=e.i(592968),g=e.i(981339),_=e.i(790848),j=e.i(245704),y=e.i(602869),b=e.i(808613),f=e.i(199133),v=e.i(311451),k=e.i(280898),N=e.i(91739),w=e.i(262218),C=e.i(312361),S=e.i(28651),I=e.i(888259),T=e.i(555987),A=e.i(826910),F=e.i(438957),L=e.i(983561),D=e.i(477189),M=e.i(827252),P=e.i(364769),R=e.i(135214),U=e.i(355619),O=e.i(663435),E=e.i(362024),q=e.i(770914),B=e.i(464571),V=e.i(646563),$=e.i(564897);let H={basic:{key:"basic",title:"Basic Information",defaultExpanded:!0,fields:[{name:"name",label:"Display Name",type:"text",required:!0,placeholder:"e.g., Customer Support Agent"},{name:"description",label:"Description",type:"textarea",required:!0,placeholder:"Describe what this agent does...",rows:3},{name:"url",label:"URL",type:"url",required:!1,placeholder:"http://localhost:9999/",tooltip:"Base URL where the agent is hosted (optional)"},{name:"version",label:"Version",type:"text",placeholder:"1.0.0",defaultValue:"1.0.0"},{name:"protocolVersion",label:"Protocol Version",type:"select",options:["1.0","0.3"],defaultValue:"1.0",tooltip:"The A2A protocol version LiteLLM serves to clients for this agent. LiteLLM converts the upstream agent's responses to this version, so clients always see the version you pick here regardless of the original agent's version.",helpText:"LiteLLM serves this version to clients and converts the upstream agent's responses to match it, regardless of the original agent's version."}]},skills:{key:"skills",title:"Skills",fields:[{name:"skills",label:"Skills",type:"list",defaultValue:[]}]},capabilities:{key:"capabilities",title:"Capabilities",fields:[{name:"streaming",label:"Streaming",type:"switch",defaultValue:!1},{name:"pushNotifications",label:"Push Notifications",type:"switch"},{name:"stateTransitionHistory",label:"State Transition History",type:"switch"}]},optional:{key:"optional",title:"Optional Settings",fields:[{name:"iconUrl",label:"Icon URL",type:"url",placeholder:"https://example.com/icon.png"},{name:"documentationUrl",label:"Documentation URL",type:"url",placeholder:"https://docs.example.com"},{name:"supportsAuthenticatedExtendedCard",label:"Supports Authenticated Extended Card",type:"switch"}]},litellm:{key:"litellm",title:"LiteLLM Parameters",fields:[{name:"model",label:"Model (Optional)",type:"text"},{name:"make_public",label:"Make Public",type:"switch"}]},cost:{key:"cost",title:"Cost Configuration",fields:[{name:"cost_per_query",label:"Cost Per Query ($)",type:"text",placeholder:"0.0",tooltip:"Fixed cost per query"},{name:"input_cost_per_token",label:"Input Cost Per Token ($)",type:"text",placeholder:"0.000001",tooltip:"Cost per input token"},{name:"output_cost_per_token",label:"Output Cost Per Token ($)",type:"text",placeholder:"0.000002",tooltip:"Cost per output token"}]},tracing:{key:"tracing",title:"Tracing",fields:[{name:"enable_tracing",label:"Enable Tracing",type:"switch",defaultValue:!1,tooltip:"Enable request tracing for this agent"}]}},z="Skill ID",G=!0,K="e.g., hello_world",W="Skill Name",Y=!0,J="e.g., Returns hello world",Q="Description",X=!0,Z="What this skill does",ee=2,et="Tags",es=!0,ea="Type a tag and press Enter",el="Examples",ei="Type an example and press Enter",er=(e,t)=>{let s={agent_name:e.agent_name,agent_card_params:{protocolVersion:e.protocolVersion||"1.0",name:e.name||e.agent_name,description:e.description||"",url:e.url||"",version:e.version||"1.0.0",defaultInputModes:t?.agent_card_params?.defaultInputModes||["text"],defaultOutputModes:t?.agent_card_params?.defaultOutputModes||["text"],capabilities:{streaming:!0===e.streaming,...void 0!==e.pushNotifications&&{pushNotifications:e.pushNotifications},...void 0!==e.stateTransitionHistory&&{stateTransitionHistory:e.stateTransitionHistory}},skills:e.skills||[],...e.iconUrl&&{iconUrl:e.iconUrl},...e.documentationUrl&&{documentationUrl:e.documentationUrl},...void 0!==e.supportsAuthenticatedExtendedCard&&{supportsAuthenticatedExtendedCard:e.supportsAuthenticatedExtendedCard}}},a={};if(e.model&&(a.model=e.model),void 0!==e.make_public&&(a.make_public=e.make_public),e.cost_per_query&&(a.cost_per_query=parseFloat(e.cost_per_query)),e.input_cost_per_token&&(a.input_cost_per_token=parseFloat(e.input_cost_per_token)),e.output_cost_per_token&&(a.output_cost_per_token=parseFloat(e.output_cost_per_token)),Object.keys(a).length>0&&(s.litellm_params=a),null!=e.tpm_limit&&(s.tpm_limit=e.tpm_limit),null!=e.rpm_limit&&(s.rpm_limit=e.rpm_limit),null!=e.session_tpm_limit&&(s.session_tpm_limit=e.session_tpm_limit),null!=e.session_rpm_limit&&(s.session_rpm_limit=e.session_rpm_limit),Array.isArray(e.static_headers)&&e.static_headers.length>0){let t={};e.static_headers.forEach(e=>{let s=e?.header?.trim();s&&(t[s]=e?.value??"")}),Object.keys(t).length>0&&(s.static_headers=t)}return Array.isArray(e.extra_headers)&&e.extra_headers.length>0&&(s.extra_headers=e.extra_headers),s},en=e=>{let t=e.agent_card_params?.skills?.map(e=>({...e,tags:e.tags,examples:e.examples||[]}))||[];return{agent_name:e.agent_name,name:e.agent_card_params?.name,description:e.agent_card_params?.description,url:e.agent_card_params?.url,version:e.agent_card_params?.version,protocolVersion:e.agent_card_params?.protocolVersion,streaming:e.agent_card_params?.capabilities?.streaming,pushNotifications:e.agent_card_params?.capabilities?.pushNotifications,stateTransitionHistory:e.agent_card_params?.capabilities?.stateTransitionHistory,skills:t,iconUrl:e.agent_card_params?.iconUrl,documentationUrl:e.agent_card_params?.documentationUrl,supportsAuthenticatedExtendedCard:e.agent_card_params?.supportsAuthenticatedExtendedCard,model:e.litellm_params?.model,make_public:e.litellm_params?.make_public,cost_per_query:e.litellm_params?.cost_per_query,input_cost_per_token:e.litellm_params?.input_cost_per_token,output_cost_per_token:e.litellm_params?.output_cost_per_token,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,session_tpm_limit:e.session_tpm_limit,session_rpm_limit:e.session_rpm_limit,static_headers:e.static_headers?Object.entries(e.static_headers).map(([e,t])=>({header:e,value:t})):[],extra_headers:e.extra_headers??[]}},eo=()=>(0,t.jsx)(t.Fragment,{children:H.cost.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,tooltip:e.tooltip,children:(0,t.jsx)(v.Input,{placeholder:e.placeholder,type:"number",step:"0.000001"})},e.name))}),{Panel:ec}=E.Collapse,ed=({showAgentName:e=!0,visiblePanels:s})=>{let a=e=>!s||s.includes(e);return(0,t.jsxs)(t.Fragment,{children:[e&&(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter a unique agent name"}],tooltip:"Unique identifier for the agent",children:(0,t.jsx)(v.Input,{placeholder:"e.g., customer-support-agent"})}),(0,t.jsxs)(E.Collapse,{defaultActiveKey:["basic"],style:{marginBottom:16},children:[a(H.basic.key)&&(0,t.jsx)(ec,{header:`${H.basic.title} (Required)`,children:H.basic.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,rules:e.required?[{required:!0,message:`Please enter ${e.label.toLowerCase()}`}]:void 0,tooltip:e.tooltip,extra:e.helpText,children:"textarea"===e.type?(0,t.jsx)(v.Input.TextArea,{rows:e.rows,placeholder:e.placeholder}):"select"===e.type?(0,t.jsx)(f.Select,{placeholder:e.placeholder,children:(e.options??[]).map(e=>(0,t.jsx)(f.Select.Option,{value:e,children:e},e))}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.basic.key),a(H.skills.key)&&(0,t.jsx)(ec,{header:`${H.skills.title}`,children:(0,t.jsx)(b.Form.List,{name:"skills",children:(e,{add:s,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(e=>(0,t.jsxs)("div",{style:{marginBottom:16,padding:16,border:"1px solid #d9d9d9",borderRadius:4},children:[(0,t.jsx)(b.Form.Item,{...e,label:z,name:[e.name,"id"],rules:[{required:G,message:"Required"}],children:(0,t.jsx)(v.Input,{placeholder:K})}),(0,t.jsx)(b.Form.Item,{...e,label:W,name:[e.name,"name"],rules:[{required:Y,message:"Required"}],children:(0,t.jsx)(v.Input,{placeholder:J})}),(0,t.jsx)(b.Form.Item,{...e,label:Q,name:[e.name,"description"],rules:[{required:X,message:"Required"}],children:(0,t.jsx)(v.Input.TextArea,{rows:ee,placeholder:Z})}),(0,t.jsx)(b.Form.Item,{...e,label:et,name:[e.name,"tags"],rules:[{required:es,message:"Required"}],children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},tokenSeparators:[","],placeholder:ea})}),(0,t.jsx)(b.Form.Item,{...e,label:el,name:[e.name,"examples"],children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},tokenSeparators:[","],placeholder:ei})}),(0,t.jsx)(B.Button,{type:"link",danger:!0,onClick:()=>a(e.name),icon:(0,t.jsx)($.MinusCircleOutlined,{}),children:"Remove Skill"})]},e.key)),(0,t.jsx)(B.Button,{type:"dashed",onClick:()=>s(),icon:(0,t.jsx)(V.PlusOutlined,{}),style:{width:"100%"},children:"Add Skill"})]})})},H.skills.key),a(H.capabilities.key)&&(0,t.jsx)(ec,{header:H.capabilities.title,children:H.capabilities.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"checked",children:(0,t.jsx)(_.Switch,{})},e.name))},H.capabilities.key),a(H.optional.key)&&(0,t.jsx)(ec,{header:H.optional.title,children:H.optional.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"switch"===e.type?"checked":void 0,children:"switch"===e.type?(0,t.jsx)(_.Switch,{}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.optional.key),a(H.cost.key)&&(0,t.jsx)(ec,{header:H.cost.title,children:(0,t.jsx)(eo,{})},H.cost.key),a(H.litellm.key)&&(0,t.jsx)(ec,{header:H.litellm.title,children:H.litellm.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"switch"===e.type?"checked":void 0,children:"switch"===e.type?(0,t.jsx)(_.Switch,{}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.litellm.key),a("auth_headers")&&(0,t.jsxs)(ec,{header:"Authentication Headers",children:[(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Static Headers"," ",(0,t.jsx)(x.Tooltip,{title:"Headers always sent to the backend agent, regardless of the client request. Admin-configured, static wins on conflict.",children:(0,t.jsx)(M.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),children:(0,t.jsx)(b.Form.List,{name:"static_headers",children:(e,{add:s,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(({key:e,name:s,...l})=>(0,t.jsxs)(q.Space,{style:{display:"flex",marginBottom:8},align:"baseline",children:[(0,t.jsx)(b.Form.Item,{...l,name:[s,"header"],rules:[{required:!0,message:"Header name required"}],children:(0,t.jsx)(v.Input,{placeholder:"Header name (e.g. Authorization)",style:{width:220}})}),(0,t.jsx)(b.Form.Item,{...l,name:[s,"value"],rules:[{required:!0,message:"Value required"}],children:(0,t.jsx)(v.Input,{placeholder:"Value (e.g. Bearer token123)",style:{width:260}})}),(0,t.jsx)($.MinusCircleOutlined,{onClick:()=>a(s),style:{color:"#ff4d4f"}})]},e)),(0,t.jsx)(B.Button,{type:"dashed",onClick:()=>s(),icon:(0,t.jsx)(V.PlusOutlined,{}),style:{width:"100%"},children:"Add Static Header"})]})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Forward Client Headers"," ",(0,t.jsx)(x.Tooltip,{title:"Header names to extract from the client's request and forward to the agent. Type a name and press Enter.",children:(0,t.jsx)(M.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),name:"extra_headers",children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},placeholder:"e.g. x-api-key, Authorization",tokenSeparators:[","]})})]},"auth_headers")]})]})};var em=e.i(536916),ep=e.i(21548),eu=e.i(482725),eh=e.i(898586);e.i(247167);var ex=e.i(931067);let eg={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z",fill:e}},{tag:"path",attrs:{d:"M512 140c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm193.4 225.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.3 0 19.9 5 25.9 13.3l71.2 98.8 157.2-218c6-8.4 15.7-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.4 12.7z",fill:t}},{tag:"path",attrs:{d:"M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z",fill:e}}]}},name:"check-circle",theme:"twotone"};var e_=e.i(9583),ej=s.forwardRef(function(e,t){return s.createElement(e_.default,(0,ex.default)({},e,{ref:t,icon:eg}))}),ey=e.i(596239),eb=e.i(91979),ef=e.i(928685);let ev=(e,t)=>e?.id??e?.name??`skill-${t}`,ek=["streaming"],eN=e=>e?ek.reduce((t,s)=>(s in e&&(t[s]=!!e[s]),t),{}):{},ew=(e,t)=>t?{...e,agent_card_params:{...e.agent_card_params,name:t.name??e.agent_card_params?.name,description:t.description??e.agent_card_params?.description,...Array.isArray(t.skills)&&{skills:t.skills},...t.capabilities&&{capabilities:t.capabilities},...Array.isArray(t.defaultInputModes)&&t.defaultInputModes.length>0&&{defaultInputModes:t.defaultInputModes},...Array.isArray(t.defaultOutputModes)&&t.defaultOutputModes.length>0&&{defaultOutputModes:t.defaultOutputModes},...t.provider&&{provider:t.provider},...t.iconUrl&&{iconUrl:t.iconUrl},...t.documentationUrl&&{documentationUrl:t.documentationUrl}}}:e,eC=(e,t,s)=>{let a=e=>(e??"").toString().trim();if("langgraph"===e){let e=a(t.api_base).replace(/\/+$/,""),s=a(t.assistant_id);if(!e||!s)return;let l=`?assistant_id=${encodeURIComponent(s)}`;return{url:e,discovery_mode:"langgraph_platform",params:{assistant_id:s},display_url:`${e}/.well-known/agent-card.json${l}`}}if("a2a"===e||s?.use_a2a_form_fields){let e=a(t.url).replace(/\/+$/,"");if(!e)return;return{url:e,discovery_mode:"well_known_fallback",display_url:`${e}/.well-known/agent-card.json`}}},{Text:eS,Paragraph:eI}=eh.Typography,{Panel:eT}=E.Collapse,eA=({accessToken:e,onApply:a,discoveryRequest:l,savedAgentCard:i})=>{let[r,n]=(0,s.useState)(""),[o,c]=(0,s.useState)(!1),[d,m]=(0,s.useState)(null),[p,u]=(0,s.useState)(null),g=void 0!==l,j=g?l.url:r,[b,f]=(0,s.useState)(""),[k,N]=(0,s.useState)(""),[C,S]=(0,s.useState)(new Set),[I,T]=(0,s.useState)({}),A=(0,s.useRef)(a);A.current=a;let F=(0,s.useRef)(0),L=(0,s.useRef)(null),D=(0,s.useRef)(l);D.current=l;let P=(0,s.useRef)(i);P.current=i;let R=l?.discovery_mode,U=(0,s.useMemo)(()=>JSON.stringify(l?.params??null),[l?.params]),O=(0,s.useCallback)(async()=>{if(!e){m("No access token available"),A.current(null);return}let t=j.trim();if(!t){m(g?"Fill in the agent's connection details above first":"Enter the agent's base URL first"),u(null),A.current(null);return}let s=D.current,a=++F.current;c(!0),m(null);try{var l;let i,r,n,o=await (0,y.discoverAgentCardCall)(e,t,g&&s?{discovery_mode:s.discovery_mode,params:s.params}:void 0);if(a!==F.current)return;L.current=null,u(o.agent_card),l=o.agent_card,n=(i=P.current)?((e,t)=>{let s=e.skills??[],a=t?.skills??[],l=new Set(a.map(e=>e?.id).filter(Boolean)),i=new Set(a.map(e=>e?.name).filter(Boolean)),r=new Set;s.forEach((e,t)=>{let s=ev(e,t),a=e.id&&l.has(e.id),n=e.name&&i.has(e.name);(a||n)&&r.add(s)});let n=eN(e.capabilities);if(t?.capabilities)for(let e of ek)e in t.capabilities&&(n[e]=!!t.capabilities[e]);return{editedName:t?.name??e.name??"",editedDescription:t?.description??e.description??"",selectedSkillIds:r,selectedCapabilities:n}})(l,i):(r=l.skills??[],{editedName:l.name??"",editedDescription:l.description??"",selectedSkillIds:new Set(r.map((e,t)=>ev(e,t))),selectedCapabilities:eN(l.capabilities)}),f(n.editedName),N(n.editedDescription),S(n.selectedSkillIds),T(n.selectedCapabilities)}catch(e){if(a!==F.current)return;m(e?.message?String(e.message):"Failed to discover agent card"),u(null),L.current=null,A.current(null)}finally{a===F.current&&c(!1)}},[e,j,g,R,U]);(0,s.useEffect)(()=>{if(!e)return;if(!j.trim()){u(null),m(null),L.current=null,A.current(null);return}let t=window.setTimeout(()=>{O()},400);return()=>window.clearTimeout(t)},[e,j,O]);let V=(0,s.useCallback)(()=>{if(!p)return null;let e=(p.skills??[]).filter((e,t)=>C.has(ev(e,t))),t={...p,name:b,description:k,skills:e,capabilities:{...I}};return{raw_card:p,selected_card:t,upstream_url:j.trim()}},[p,k,b,j,I,C]);(0,s.useEffect)(()=>{if(!p)return;let e=V(),t=JSON.stringify(e);L.current!==t&&(L.current=t,A.current(e))},[V,p]);let $=p?.skills?.length??0,H=C.size;return(0,t.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4 bg-gray-50 mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-2",children:[(0,t.jsx)(ey.LinkOutlined,{className:"text-indigo-600"}),(0,t.jsx)(eS,{strong:!0,children:"Discover from agent URL"}),(0,t.jsx)(x.Tooltip,{title:"LiteLLM will fetch /.well-known/agent-card.json from this URL and let you pick which skills and capabilities to expose through the proxy.",children:(0,t.jsx)(M.InfoCircleOutlined,{className:"text-gray-400"})})]}),g?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eI,{className:"text-xs text-gray-500 mb-2",children:"Using the connection details you entered above. We'll fetch:"}),(0,t.jsx)("div",{className:"bg-white border border-gray-200 rounded-sm px-3 py-2 mb-3 font-mono text-xs text-gray-700 break-all",children:l.display_url||j||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Fill in the fields above first"})}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(B.Button,{type:"primary",icon:p?(0,t.jsx)(eb.ReloadOutlined,{}):(0,t.jsx)(ef.SearchOutlined,{}),loading:o,onClick:O,disabled:!j.trim(),children:p?"Re-discover":"Discover"})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(eI,{className:"text-xs text-gray-500 mb-3",children:["Paste the upstream agent's base URL. We'll try ",(0,t.jsx)("code",{children:"/.well-known/agent-card.json"}),","," ",(0,t.jsx)("code",{children:"/.well-known/agent.json"}),", and ",(0,t.jsx)("code",{children:"/agent.json"})," in order."]}),(0,t.jsxs)(q.Space.Compact,{style:{width:"100%"},children:[(0,t.jsx)(v.Input,{placeholder:"https://upstream-agent.example.com",value:r,onChange:e=>n(e.target.value),onPressEnter:O,allowClear:!0,disabled:o}),(0,t.jsx)(B.Button,{type:"primary",icon:p?(0,t.jsx)(eb.ReloadOutlined,{}):(0,t.jsx)(ef.SearchOutlined,{}),loading:o,onClick:O,children:p?"Re-discover":"Discover"})]})]}),d&&(0,t.jsx)(h.Alert,{className:"mt-3",type:"error",message:"Discovery failed",description:d,showIcon:!0,closable:!0,onClose:()=>m(null)}),o&&!p&&(0,t.jsx)("div",{className:"flex items-center justify-center py-8",children:(0,t.jsx)(eu.Spin,{})}),p&&(0,t.jsxs)("div",{className:"mt-4 bg-white border border-gray-200 rounded-lg p-4",children:[(0,t.jsx)("div",{className:"flex items-center justify-between mb-3",children:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(ej,{twoToneColor:"#52c41a"}),(0,t.jsx)(eS,{strong:!0,children:"Upstream card loaded"}),p.version&&(0,t.jsxs)(w.Tag,{color:"blue",children:["v",p.version]}),p.provider?.organization&&(0,t.jsx)(w.Tag,{color:"purple",children:p.provider.organization})]})}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3 mb-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-600 block mb-1",children:"Name (shown to API clients)"}),(0,t.jsx)(v.Input,{value:b,onChange:e=>f(e.target.value),placeholder:"Agent name"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-600 block mb-1",children:"Description"}),(0,t.jsx)(v.Input.TextArea,{value:k,onChange:e=>N(e.target.value),rows:2,placeholder:"What this agent does"})]})]}),(0,t.jsxs)(E.Collapse,{defaultActiveKey:["skills","capabilities"],ghost:!0,className:"bg-transparent",children:[(0,t.jsx)(eT,{header:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(eS,{strong:!0,children:"Skills"}),(0,t.jsxs)(w.Tag,{children:[H," / ",$," selected"]})]}),children:0===$?(0,t.jsx)(ep.Empty,{image:ep.Empty.PRESENTED_IMAGE_SIMPLE,description:"Upstream card has no skills"}):(0,t.jsx)("div",{className:"space-y-2",children:(p.skills??[]).map((e,s)=>{let a=ev(e,s),l=C.has(a);return(0,t.jsxs)("label",{className:`flex items-start gap-3 p-3 border rounded cursor-pointer transition-colors ${l?"border-indigo-300 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,children:[(0,t.jsx)(em.Checkbox,{checked:l,onChange:e=>{var t;return t=e.target.checked,void S(e=>{let s=new Set(e);return t?s.add(a):s.delete(a),s})}}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[(0,t.jsx)(eS,{strong:!0,children:e.name||a}),e.id&&(0,t.jsx)(w.Tag,{style:{marginLeft:0},children:e.id}),(e.tags??[]).map(e=>(0,t.jsx)(w.Tag,{color:"geekblue",children:e},e))]}),e.description&&(0,t.jsx)(eI,{className:"text-xs text-gray-500 mt-1 mb-0",ellipsis:{rows:2,expandable:!0,symbol:"more"},children:e.description})]})]},a)})})},"skills"),(0,t.jsx)(eT,{header:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(eS,{strong:!0,children:"Capabilities"}),(0,t.jsx)(x.Tooltip,{title:"Only capabilities LiteLLM can faithfully proxy today are listed. Others (push notifications, extensions) are coming soon.",children:(0,t.jsx)(M.InfoCircleOutlined,{className:"text-gray-400"})})]}),children:(0,t.jsx)("div",{className:"space-y-2",children:ek.map(e=>{let s=!!p.capabilities?.[e];return(0,t.jsxs)("div",{className:"flex items-center justify-between p-2 border border-gray-200 rounded-sm bg-white",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(eS,{strong:!0,className:"capitalize",children:e}),!s&&(0,t.jsx)(w.Tag,{className:"ml-2",color:"default",children:"not advertised upstream"})]}),(0,t.jsx)(_.Switch,{checked:!!I[e],onChange:t=>T(s=>({...s,[e]:t}))})]},e)})})},"capabilities")]})]})]})},{Panel:eF}=E.Collapse,eL=(e,t)=>{let s={...t.litellm_params_template||{}};for(let a of t.credential_fields){let t=e[a.key];t&&!1!==a.include_in_litellm_params&&(s[a.key]=t)}if(e.cost_per_query&&(s.cost_per_query=parseFloat(e.cost_per_query)),e.input_cost_per_token&&(s.input_cost_per_token=parseFloat(e.input_cost_per_token)),e.output_cost_per_token&&(s.output_cost_per_token=parseFloat(e.output_cost_per_token)),t.model_template){let a=t.model_template;for(let s of t.credential_fields){let t=`{${s.key}}`;a.includes(t)&&e[s.key]&&(a=a.replace(t,e[s.key]))}s.model=a}let a={agent_name:e.agent_name,agent_card_params:{protocolVersion:"1.0",name:e.display_name||e.agent_name,description:e.description||`${t.agent_type_display_name} agent`,url:e.api_base||"",version:"1.0.0",defaultInputModes:["text"],defaultOutputModes:["text"],capabilities:{streaming:!0},skills:[{id:"chat",name:"Chat",description:"General chat capability",tags:["chat","conversation"]}]},litellm_params:s};return null!=e.tpm_limit&&(a.tpm_limit=e.tpm_limit),null!=e.rpm_limit&&(a.rpm_limit=e.rpm_limit),null!=e.session_tpm_limit&&(a.session_tpm_limit=e.session_tpm_limit),null!=e.session_rpm_limit&&(a.session_rpm_limit=e.session_rpm_limit),a},eD=({agentTypeInfo:e})=>(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter a unique agent name"}],tooltip:"Unique identifier for the agent",children:(0,t.jsx)(v.Input,{placeholder:"e.g., my-langgraph-agent"})}),(0,t.jsx)(b.Form.Item,{label:"Description",name:"description",tooltip:"Brief description of what this agent does",children:(0,t.jsx)(v.Input.TextArea,{rows:2,placeholder:"Describe what this agent does..."})}),e.credential_fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:`Please enter ${e.label}`}]:void 0,tooltip:e.tooltip,initialValue:e.default_value,children:"password"===e.field_type?(0,t.jsx)(v.Input.Password,{placeholder:e.placeholder||""}):"textarea"===e.field_type?(0,t.jsx)(v.Input.TextArea,{rows:3,placeholder:e.placeholder||""}):"select"===e.field_type&&e.options?(0,t.jsx)(f.Select,{placeholder:e.placeholder||"",children:e.options.map(e=>(0,t.jsx)(f.Select.Option,{value:e,children:e},e))}):(0,t.jsx)(v.Input,{placeholder:e.placeholder||""})},e.key)),(0,t.jsx)(E.Collapse,{style:{marginBottom:16},children:(0,t.jsx)(eF,{header:H.cost.title,children:(0,t.jsx)(eo,{})},H.cost.key)})]});var eM=e.i(75921),eP=e.i(390605),eR=e.i(891547);let{Step:eU}=k.Steps,eO="custom",eE=({visible:e,onClose:l,accessToken:i,onSuccess:r,teams:n})=>{let o,c,{userId:d,userRole:m}=(0,R.default)(),[p]=b.Form.useForm(),[h,x]=(0,s.useState)(0),[g,j]=(0,s.useState)(!1),[E,q]=(0,s.useState)("a2a"),[B,V]=(0,s.useState)([]),[$,z]=(0,s.useState)(!1),[G,K]=(0,s.useState)("create_new"),[W,Y]=(0,s.useState)(""),[J,Q]=(0,s.useState)([]),[X,Z]=(0,s.useState)([]),[ee,et]=(0,s.useState)(null),[es,ea]=(0,s.useState)(!1),[el,ei]=(0,s.useState)([]),[en,eo]=(0,s.useState)(!1),[ec,em]=(0,s.useState)([]),[ep,eu]=(0,s.useState)(!1),[eh,ex]=(0,s.useState)(""),[eg,e_]=(0,s.useState)(null),[ej,ey]=(0,s.useState)(null),[eb,ef]=(0,s.useState)(!1),[ev,ek]=(0,s.useState)(!1),[eN,eS]=(0,s.useState)(null),[eI,eT]=(0,s.useState)(null),[eF,eE]=(0,s.useState)(null);(0,s.useEffect)(()=>{(async()=>{z(!0);try{let e=await (0,y.getAgentCreateMetadata)();V(e)}catch(e){console.error("Error fetching agent metadata:",e)}finally{z(!1)}})()},[]),(0,s.useEffect)(()=>{3===h&&i&&0===X.length&&(async()=>{ea(!0);try{let e=await (0,y.keyListCall)(i,null,null,null,null,null,1,100);Z(e?.keys||[])}catch(e){console.error("Error fetching keys:",e)}finally{ea(!1)}})()},[h,i]),(0,s.useEffect)(()=>{if(1!==h&&3!==h||!i||!d||!m)return;let e=!1;return eo(!0),(0,y.modelAvailableCall)(i,d,m).then(t=>{e||ei((t?.data??(Array.isArray(t)?t:[])).map(e=>e.id??e.model_name).filter(Boolean))}).catch(t=>{e||console.error("Error fetching models:",t)}).finally(()=>{e||eo(!1)}),()=>{e=!0}},[h,i,d,m]),(0,s.useEffect)(()=>{if(1!==h||!i)return;let e=!1;return eu(!0),(0,y.getAgentsList)(i).then(t=>{e||em((t?.agents??[]).map(e=>({agent_id:e.agent_id,agent_name:e.agent_name})))}).catch(t=>{e||console.error("Error fetching agents:",t)}).finally(()=>{e||eu(!1)}),()=>{e=!0}},[h,i]);let eq=B.find(e=>e.agent_type===E),eB=b.Form.useWatch([],p),eV=s.default.useMemo(()=>eC(E,eB||{},eq),[eB,eq,E]),e$=async()=>{try{if(0===h){await p.validateFields();let e=p.getFieldValue("agent_name");e&&!W&&Y(`${e}-key`)}x(e=>e+1)}catch{}},eH=async()=>{if(!i)return void I.default.error("No access token available");j(!0);try{await p.validateFields();let e={...p.getFieldsValue(!0)},t=(e=>{let t;if(E===eO)return{agent_name:e.agent_name,agent_card_params:{protocolVersion:"1.0",name:e.agent_name,description:e.description||"",url:"",version:"1.0.0",defaultInputModes:["text"],defaultOutputModes:["text"],capabilities:{streaming:!1},skills:[]}};if("a2a"===E)t=er(e);else if(eq?.use_a2a_form_fields)for(let s of(t=er(e),eq.litellm_params_template&&(t.litellm_params={...t.litellm_params,...eq.litellm_params_template}),eq.credential_fields)){let a=e[s.key];a&&!1!==s.include_in_litellm_params&&(t.litellm_params[s.key]=a)}else{if(!eq)return null;t=eL(e,eq)}return ew(t,eF?.selected_card)})(e);if(!t){I.default.error("Failed to build agent data"),j(!1);return}let s=e.allowed_mcp_servers_and_groups,a=e.mcp_tool_permissions||{},l=e.entitlement_models||[],n=e.entitlement_agents||[];(s?.servers?.length>0||s?.accessGroups?.length>0||Object.keys(a).length>0||l.length>0||n.length>0)&&(t.object_permission={},s?.servers?.length>0&&(t.object_permission.mcp_servers=s.servers),s?.accessGroups?.length>0&&(t.object_permission.mcp_access_groups=s.accessGroups),Object.keys(a).length>0&&(t.object_permission.mcp_tool_permissions=a),l.length>0&&(t.object_permission.models=l),n.length>0&&(t.object_permission.agents=n)),(eb||ev)&&(t.litellm_params||(t.litellm_params={}),eb&&(t.litellm_params.require_trace_id_on_calls_to_agent=!0),ev&&(t.litellm_params.require_trace_id_on_calls_by_agent=!0,eN&&(t.litellm_params.max_iterations=eN),eI&&(t.litellm_params.max_budget_per_session=eI)));let o=e.guardrails||[];o.length>0&&(t.litellm_params||(t.litellm_params={}),t.litellm_params.guardrails=o);let c=e.team_id||null;c&&(t.team_id=c);let d=await (0,y.createAgentCall)(i,t),m=d.agent_id,u=d.agent_name||e.agent_name||m;if(ex(u),"create_new"===G&&W){let e=await (0,y.keyCreateForAgentCall)(i,m,W,J,void 0,c);e_(e.key||null)}else if("existing_key"===G){if(!ee){I.default.error("Please select an existing key to assign"),j(!1);return}await (0,y.keyUpdateCall)(i,{key:ee,agent_id:m});let e=X.find(e=>e.token===ee);ey(e?.key_alias||ee.slice(0,12)+"…")}x(4),r()}catch(t){console.error("Error creating agent:",t);let e=t instanceof Error?t.message:String(t);I.default.error(e?`Failed to create agent: ${e}`:"Failed to create agent")}finally{j(!1)}},ez=()=>{p.resetFields(),q("a2a"),x(0),K("create_new"),Y(""),Q([]),et(null),ex(""),e_(null),ey(null),ef(!1),ek(!1),eS(null),eT(null),eE(null),l()},eG=e=>{q(e),p.resetFields(),eE(null)},eK=E===eO?null:eq?.logo_url||B.find(e=>"a2a"===e.agent_type)?.logo_url;return(0,t.jsx)(u.Modal,{title:(0,t.jsxs)("div",{className:"flex items-center space-x-3 pb-4 border-b border-gray-100",children:[eK&&h<1&&(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(eK),alt:"Agent",className:"w-6 h-6 object-contain"}),(0,t.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:"Add New Agent"})]}),open:e,onCancel:ez,footer:null,width:900,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)(k.Steps,{current:h,size:"small",className:"mb-8",children:[(0,t.jsx)(eU,{title:"Configure"}),(0,t.jsx)(eU,{title:"Entitlements"}),(0,t.jsx)(eU,{title:"Governance"}),(0,t.jsx)(eU,{title:"Agent Management"}),(0,t.jsx)(eU,{title:"Ready"})]}),(0,t.jsxs)(b.Form,{form:p,layout:"vertical",initialValues:"a2a"===E?{...(o={defaultInputModes:["text"],defaultOutputModes:["text"]},Object.values(H).forEach(e=>{e.fields.forEach(e=>{void 0!==e.defaultValue&&(o[e.name]=e.defaultValue)})}),o),allowed_mcp_servers_and_groups:{servers:[],accessGroups:[]},mcp_tool_permissions:{},entitlement_models:[],entitlement_agents:[],guardrails:[]}:{allowed_mcp_servers_and_groups:{servers:[],accessGroups:[]},mcp_tool_permissions:{},entitlement_models:[],entitlement_agents:[],guardrails:[]},className:"space-y-4",children:[0===h&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Agent Type"}),required:!0,tooltip:"Select the type of agent you want to create",children:(0,t.jsx)(f.Select,{value:E,onChange:eG,size:"large",style:{width:"100%"},optionLabelProp:"label",dropdownRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,(0,t.jsx)(C.Divider,{style:{margin:"4px 0"}}),(0,t.jsxs)("div",{className:"px-2 py-1",children:[(0,t.jsx)("div",{className:"text-xs text-gray-400 font-medium mb-1 uppercase tracking-wide px-2",children:"Not listed?"}),(0,t.jsxs)("div",{className:`flex items-center gap-3 px-2 py-2 rounded cursor-pointer transition-colors ${E===eO?"bg-amber-50":"hover:bg-amber-50"}`,onClick:()=>eG(eO),children:[(0,t.jsx)(D.AppstoreOutlined,{className:"text-amber-600 text-lg"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"font-medium text-amber-700",children:"Custom / Other"}),(0,t.jsx)(w.Tag,{color:"orange",style:{fontSize:10,padding:"0 4px"},children:"GENERIC"})]}),(0,t.jsx)("div",{className:"text-xs text-amber-600",children:"For agents that don't follow a standard protocol — just needs a virtual key"})]})]})]})]}),children:B.map(e=>(0,t.jsx)(f.Select.Option,{value:e.agent_type,label:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(e.logo_url)??"",alt:"",className:"w-4 h-4 object-contain"}),(0,t.jsx)("span",{children:e.agent_type_display_name})]}),children:(0,t.jsxs)("div",{className:"flex items-center gap-3 py-1",children:[(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(e.logo_url)??"",alt:e.agent_type_display_name,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"font-medium",children:e.agent_type_display_name}),e.description&&(0,t.jsx)("div",{className:"text-xs text-gray-500",children:e.description})]})]})},e.agent_type))})}),(0,t.jsxs)("div",{className:"mt-4",children:[E===eO?(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter an agent name"}],children:(0,t.jsx)(v.Input,{placeholder:"e.g. my-custom-agent"})}),(0,t.jsx)(b.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(v.Input.TextArea,{placeholder:"Describe what this agent does…",rows:3})})]}):"a2a"===E?(0,t.jsx)(ed,{showAgentName:!0}):eq?.use_a2a_form_fields?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(ed,{showAgentName:!0}),eq.credential_fields.length>0&&(0,t.jsxs)("div",{className:"mt-4 p-4 border border-gray-200 rounded-lg",children:[(0,t.jsxs)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:[eq.agent_type_display_name," Settings"]}),eq.credential_fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:`Please enter ${e.label}`}]:void 0,tooltip:e.tooltip,initialValue:e.default_value,children:"password"===e.field_type?(0,t.jsx)(v.Input.Password,{placeholder:e.placeholder||""}):(0,t.jsx)(v.Input,{placeholder:e.placeholder||""})},e.key))]})]}):eq?(0,t.jsx)(eD,{agentTypeInfo:eq}):null,E!==eO&&(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eA,{accessToken:i,onApply:e=>{if(eE(e),!e)return;let{selected_card:t,upstream_url:s}=e,a=(t.skills??[]).map(e=>({id:e.id??"",name:e.name??"",description:e.description??"",tags:e.tags??[],examples:e.examples??[]})),l=p.getFieldValue("agent_name")||t.name||t.provider?.organization||"",i={agent_name:l,name:t.name,description:t.description,url:s,version:t.version,protocolVersion:t.protocolVersion??"1.0",streaming:!!t.capabilities?.streaming,skills:a,iconUrl:t.iconUrl,documentationUrl:t.documentationUrl};for(let e of(eq?.credential_fields??[]).map(e=>e.key).filter(e=>/(^|_)(url|api_base|endpoint)$/i.test(e)))i[e]=s;p.setFieldsValue(i),!W&&l&&Y(`${l}-key`)},discoveryRequest:eV})})]})]}),1===h&&(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"Configure which models, agents, and MCP tools this agent is allowed to use. Leave fields empty to allow all (subject to key/team permissions)."}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Allowed Models"}),name:"entitlement_models",tooltip:"Restrict which models this agent can call. Leave empty to allow all.",children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},placeholder:en?"Loading models...":"Select models (leave empty for all)",tokenSeparators:[","],loading:en,showSearch:!0,options:el.map(e=>({label:(0,U.getModelDisplayName)(e),value:e}))})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Allowed Agents (Sub-Agents)"}),name:"entitlement_agents",tooltip:"Restrict which other agents this agent can invoke as sub-agents. Leave empty to allow all.",children:(0,t.jsx)(f.Select,{mode:"multiple",style:{width:"100%"},placeholder:ep?"Loading agents...":"Select agents (leave empty for all)",loading:ep,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:ec.map(e=>({label:e.agent_name,value:e.agent_id}))})}),(0,t.jsx)(C.Divider,{className:"my-2"}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(M.InfoCircleOutlined,{title:"Select which MCP servers or access groups this agent can access",style:{marginLeft:"4px"}})]}),name:"allowed_mcp_servers_and_groups",initialValue:{servers:[],accessGroups:[]},children:(0,t.jsx)(eM.default,{onChange:e=>p.setFieldValue("allowed_mcp_servers_and_groups",e),value:p.getFieldValue("allowed_mcp_servers_and_groups")||{servers:[],accessGroups:[]},accessToken:i??"",placeholder:"Select MCP servers or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eP.default,{accessToken:i??"",selectedServers:p.getFieldValue("allowed_mcp_servers_and_groups")?.servers??[],toolPermissions:p.getFieldValue("mcp_tool_permissions")??{},onChange:e=>p.setFieldsValue({mcp_tool_permissions:e})})})})]}),2===h&&(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Tracing"}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Require x-litellm-trace-id on calls TO this agent"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Only accept this agent being invoked with a trace-id (e.g. when used as a sub-agent)."})]}),(0,t.jsx)(_.Switch,{checked:eb,onChange:ef})]}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Require x-litellm-trace-id on calls BY this agent"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Requires LLM/MCP calls made by this agent to include x-litellm-trace-id for session tracking."})]}),(0,t.jsx)(_.Switch,{checked:ev,onChange:e=>{ek(e),e||(eS(null),eT(null))}})]})]})]}),(0,t.jsx)(C.Divider,{className:"my-0"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Budgets & Rate Limits"}),(0,t.jsxs)("div",{className:"space-y-4",children:[!ev&&(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg text-sm text-yellow-800",children:'Enable "Require x-litellm-trace-id on calls BY this agent" in Tracing to configure budgets and rate limits.'}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Session Budgets"}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Max Iterations"}),(0,t.jsx)(S.InputNumber,{className:"w-full",min:1,placeholder:"e.g. 25",disabled:!ev,value:eN,onChange:e=>eS(e)}),(0,t.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"Hard cap on LLM calls per session"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Max Budget Per Session ($)"}),(0,t.jsx)(S.InputNumber,{className:"w-full",min:.01,step:.5,placeholder:"e.g. 5.00",disabled:!ev,value:eI,onChange:e=>eT(e)}),(0,t.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"Max spend per trace before returning 429"})]})]}),(0,t.jsx)(C.Divider,{className:"my-2"}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Agent Rate Limits"}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:"Global rate limits applied across all callers of this agent."}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 100000",disabled:!ev})}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 100",disabled:!ev})})]}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700 mt-4",children:"Per-Session Rate Limits"}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:"Rate limits per session (x-litellm-trace-id). Each session gets its own counters."}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"Session TPM Limit",name:"session_tpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 10000",disabled:!ev})}),(0,t.jsx)(b.Form.Item,{label:"Session RPM Limit",name:"session_rpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 20",disabled:!ev})})]})]})]}),(0,t.jsx)(C.Divider,{className:"my-0"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Guardrails"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Apply guardrails to this agent. Selected guardrails will run on all calls made by this agent."}),(0,t.jsx)(b.Form.Item,{name:"guardrails",initialValue:[],children:(0,t.jsx)(eR.default,{accessToken:i??"",value:p.getFieldValue("guardrails")??[],onChange:e=>p.setFieldsValue({guardrails:e})})})]})]}),3===h&&(c=p.getFieldValue("agent_name")||"your-agent",(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"flex justify-center mb-6",children:(0,t.jsx)(w.Tag,{icon:(0,t.jsx)(L.RobotOutlined,{}),color:"purple",className:"px-3 py-1 text-sm",children:c})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Assign to Team"}),name:"team_id",tooltip:"Optionally assign this agent to a team. The agent and its key will belong to the selected team.",children:(0,t.jsx)(O.default,{})}),(0,t.jsx)(C.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsx)("div",{className:`p-4 border-2 rounded-lg cursor-pointer transition-colors ${"create_new"===G?"border-indigo-600 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,onClick:()=>K("create_new"),children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex items-start gap-3 flex-1",children:[(0,t.jsx)(N.Radio,{value:"create_new",checked:"create_new"===G,onChange:()=>K("create_new")}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-indigo-600"}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:"Create a new key for this agent"})]}),(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"A dedicated key scoped to this agent."}),"create_new"===G&&(0,t.jsx)("div",{className:"mt-3 space-y-3",onClick:e=>e.stopPropagation(),children:(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Key Name"}),(0,t.jsx)(v.Input,{value:W,onChange:e=>Y(e.target.value),placeholder:"e.g. my-agent-key"})]})})]})]}),(0,t.jsx)(w.Tag,{color:"green",children:"Recommended"})]})}),(0,t.jsx)("div",{className:`p-4 border-2 rounded-lg cursor-pointer transition-colors ${"existing_key"===G?"border-indigo-600 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,onClick:()=>K("existing_key"),children:(0,t.jsxs)("div",{className:"flex items-start gap-3",children:[(0,t.jsx)(N.Radio,{value:"existing_key",checked:"existing_key"===G,onChange:()=>K("existing_key")}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-gray-500"}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:"Assign an existing key"})]}),(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"Re-assign a key you already have to this agent."}),"existing_key"===G&&(0,t.jsx)("div",{className:"mt-3",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(f.Select,{showSearch:!0,style:{width:"100%"},placeholder:"Search by key name…",loading:es,value:ee,onChange:e=>et(e),filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:X.map(e=>({label:e.key_alias||e.token?.slice(0,12)+"…",value:e.token}))})})]})]})})]}),(0,t.jsx)("div",{className:"text-center mt-4",children:(0,t.jsx)("button",{type:"button",className:"text-sm text-gray-500 underline hover:text-gray-700",onClick:()=>K("skip"),children:"Skip for now — I'll assign a key later"})})]})),4===h&&(0,t.jsxs)("div",{className:"text-center py-6",children:[(0,t.jsx)(A.CheckCircleFilled,{className:"text-5xl text-green-500 mb-4",style:{fontSize:48}}),(0,t.jsx)("h3",{className:"text-xl font-semibold text-gray-900 mb-2",children:"Agent Created!"}),(0,t.jsx)("div",{className:"flex justify-center mb-4",children:(0,t.jsx)(w.Tag,{icon:(0,t.jsx)(L.RobotOutlined,{}),color:"purple",className:"px-3 py-1 text-sm",children:eh})}),eg&&(0,t.jsx)("div",{className:"mt-4 text-left max-w-md mx-auto",children:(0,t.jsx)(P.default,{apiKey:eg})}),ej&&(0,t.jsxs)("p",{className:"text-sm text-gray-600 mt-2",children:["Key ",(0,t.jsx)("span",{className:"font-medium",children:ej})," has been assigned to this agent."]}),!eg&&!ej&&"skip"===G&&(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-2",children:"No key assigned. You can create one from the Virtual Keys page."})]})]}),(0,t.jsxs)("div",{className:"flex items-center justify-between pt-6 border-t border-gray-100 mt-6",children:[(0,t.jsx)("div",{children:h>0&&h<4&&(0,t.jsx)("button",{type:"button",onClick:()=>{x(e=>Math.max(0,e-1))},className:"text-sm text-gray-600 border border-gray-300 rounded-sm px-4 py-2 hover:bg-gray-50",children:"← Back"})}),(0,t.jsxs)("div",{className:"flex gap-3",children:[h<4&&(0,t.jsx)(a.Button,{variant:"secondary",onClick:ez,children:"Cancel"}),0===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),1===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),2===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),3===h&&(0,t.jsx)(a.Button,{variant:"primary",loading:g,onClick:eH,children:g?"Creating...":"Create Agent →"}),4===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:ez,children:"Done"})]})]})]})})};var eq=e.i(708347),eB=e.i(629569),eV=e.i(197647),e$=e.i(653824),eH=e.i(881073),ez=e.i(404206),eG=e.i(723731),eK=e.i(869216),eW=e.i(530212),eY=e.i(207082),eJ=e.i(20147);let{Title:eQ,Text:eX}=eh.Typography,eZ=({keys:e,isLoading:s,onKeyClick:a})=>(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eQ,{level:4,children:"Virtual Keys"}),s?(0,t.jsx)(eX,{className:"mt-2 block",children:"Loading keys..."}):0===e.length?(0,t.jsx)(eX,{className:"mt-2 block text-gray-500",children:"No virtual key assigned to this agent."}):(0,t.jsx)("div",{className:"mt-3 flex flex-col gap-2",children:e.map(e=>(0,t.jsxs)("div",{className:"flex items-center gap-3 border border-gray-100 rounded-sm px-3 py-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-gray-400"}),(0,t.jsx)("span",{className:"font-medium",children:e.key_alias||"Unnamed key"}),e.key_name&&(0,t.jsx)("span",{className:"font-mono text-xs text-gray-500",children:e.key_name}),(0,t.jsx)(x.Tooltip,{title:e.token,children:(0,t.jsxs)(B.Button,{size:"small",type:"link",className:"font-mono text-blue-500 ml-auto",onClick:()=>a(e),children:[e.token?.slice(0,12),"..."]})})]},e.token))})]}),e0=({agent:e})=>{let s=e.litellm_params;return s?.cost_per_query===void 0&&s?.input_cost_per_token===void 0&&s?.output_cost_per_token===void 0?null:(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eB.Title,{children:"Cost Configuration"}),(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:[void 0!==s.cost_per_query&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Cost Per Query",children:["$",s.cost_per_query]}),void 0!==s.input_cost_per_token&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Input Cost Per Token",children:["$",s.input_cost_per_token]}),void 0!==s.output_cost_per_token&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Output Cost Per Token",children:["$",s.output_cost_per_token]})]})]})},e1=e=>{let t=e.litellm_params?.model||"",s=e.litellm_params?.custom_llm_provider;return"langflow"===s?"langflow":"langgraph"===s?"langgraph":"azure_ai"===s?"azure_ai_foundry":"bedrock"===s?"bedrock_agentcore":t.startsWith("langflow/")?"langflow":t.startsWith("langgraph/")?"langgraph":t.startsWith("azure_ai/agents/")?"azure_ai_foundry":t.startsWith("bedrock/agentcore/")?"bedrock_agentcore":"a2a"},e2=(e,t)=>{let s={agent_name:e.agent_name,description:e.agent_card_params?.description||""};for(let a of t.credential_fields)if(!1!==a.include_in_litellm_params)s[a.key]=e.litellm_params?.[a.key]||a.default_value||"";else if(t.model_template&&e.litellm_params?.model){let l=e.litellm_params.model,i=t.model_template.split("/"),r=l.split("/");i.forEach((e,t)=>{e===`{${a.key}}`&&r[t]&&(s[a.key]=r[t])})}return s.cost_per_query=e.litellm_params?.cost_per_query,s.input_cost_per_token=e.litellm_params?.input_cost_per_token,s.output_cost_per_token=e.litellm_params?.output_cost_per_token,s},e4=({agentId:e,onClose:i,accessToken:r,isAdmin:n})=>{let[o,c]=(0,s.useState)(null),[d,m]=(0,s.useState)(null),{data:u,isLoading:h,refetch:x}=(0,eY.useKeys)(1,100,{agentID:e}),g=u?.keys??[],[_,j]=(0,s.useState)(!0),[f,k]=(0,s.useState)(!1),[N,w]=(0,s.useState)(!1),[T]=b.Form.useForm(),[A,F]=(0,s.useState)([]),[L,D]=(0,s.useState)("a2a"),[M,P]=(0,s.useState)(null);(0,s.useEffect)(()=>{(async()=>{try{let e=await (0,y.getAgentCreateMetadata)();F(e)}catch(e){console.error("Error fetching agent metadata:",e)}})()},[]),(0,s.useEffect)(()=>{R()},[e,r]);let R=async()=>{if(r){j(!0);try{let t=await (0,y.getAgentInfo)(r,e);c(t);let s=e1(t);if(D(s),"a2a"===s)T.setFieldsValue(en(t));else{let e=A.find(e=>e.agent_type===s);e?T.setFieldsValue(e2(t,e)):T.setFieldsValue(en(t))}}catch(e){console.error("Error fetching agent info:",e),I.default.error("Failed to load agent information")}finally{j(!1)}}};(0,s.useEffect)(()=>{if(o&&A.length>0){let e=e1(o);if("a2a"!==e){let t=A.find(t=>t.agent_type===e);t&&T.setFieldsValue(e2(o,t))}}},[A,o]);let U=A.find(e=>e.agent_type===L),O=b.Form.useWatch([],T),E=(0,s.useMemo)(()=>eC(L,O||{},U),[O,U,L]),q=async t=>{if(r&&o){w(!0);try{let s;"a2a"===L?s=er(t,o):U?(s=eL(t,U)).agent_name=t.agent_name:s=er(t,o),M&&(s=ew(s,M.selected_card)),await (0,y.patchAgentCall)(r,e,s),I.default.success("Agent updated successfully"),k(!1),R()}catch(e){console.error("Error updating agent:",e),I.default.error("Failed to update agent")}finally{w(!1)}}};if(_)return(0,t.jsx)("div",{className:"p-4",children:(0,t.jsx)("div",{className:"flex justify-center items-center h-64",children:(0,t.jsx)(eu.Spin,{size:"large"})})});if(!o)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)("div",{className:"text-center",children:"Agent not found"}),(0,t.jsx)(a.Button,{onClick:i,className:"mt-4",children:"Back to Agents List"})]});let V=e=>e?new Date(e).toLocaleString():"-";return d?(0,t.jsx)(eJ.default,{keyId:d.token,keyData:d,onClose:()=>m(null),onDelete:()=>{m(null),x()},teams:null,backButtonText:"Back to Agent"}):(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Button,{icon:eW.ArrowLeftIcon,variant:"light",onClick:i,className:"mb-4",children:"Back to Agents"}),(0,t.jsx)(eB.Title,{children:o.agent_name||"Unnamed Agent"}),(0,t.jsx)(p.Text,{className:"text-gray-500 font-mono",children:o.agent_id})]}),(0,t.jsxs)(e$.TabGroup,{children:[(0,t.jsxs)(eH.TabList,{className:"mb-4",children:[(0,t.jsx)(eV.Tab,{children:"Overview"},"overview"),n?(0,t.jsx)(eV.Tab,{children:"Settings"},"settings"):(0,t.jsx)(t.Fragment,{})]}),(0,t.jsxs)(eG.TabPanels,{children:[(0,t.jsxs)(ez.TabPanel,{children:[(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,children:[(0,t.jsx)(eK.Descriptions.Item,{label:"Agent ID",children:o.agent_id}),(0,t.jsx)(eK.Descriptions.Item,{label:"Agent Name",children:o.agent_name}),(0,t.jsx)(eK.Descriptions.Item,{label:"Display Name",children:o.agent_card_params?.name||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Description",children:o.agent_card_params?.description||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"URL",children:o.agent_card_params?.url||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Version",children:o.agent_card_params?.version||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Protocol Version",children:o.agent_card_params?.protocolVersion||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Streaming",children:o.agent_card_params?.capabilities?.streaming?"Yes":"No"}),o.agent_card_params?.capabilities?.pushNotifications&&(0,t.jsx)(eK.Descriptions.Item,{label:"Push Notifications",children:"Yes"}),o.agent_card_params?.capabilities?.stateTransitionHistory&&(0,t.jsx)(eK.Descriptions.Item,{label:"State Transition History",children:"Yes"}),(0,t.jsxs)(eK.Descriptions.Item,{label:"Skills",children:[o.agent_card_params?.skills?.length||0," configured"]}),o.litellm_params?.model&&(0,t.jsx)(eK.Descriptions.Item,{label:"Model",children:o.litellm_params.model}),o.litellm_params?.make_public!==void 0&&(0,t.jsx)(eK.Descriptions.Item,{label:"Make Public",children:o.litellm_params.make_public?"Yes":"No"}),o.agent_card_params?.iconUrl&&(0,t.jsx)(eK.Descriptions.Item,{label:"Icon URL",children:o.agent_card_params.iconUrl}),o.agent_card_params?.documentationUrl&&(0,t.jsx)(eK.Descriptions.Item,{label:"Documentation URL",children:o.agent_card_params.documentationUrl}),(0,t.jsx)(eK.Descriptions.Item,{label:"TPM Limit",children:o.tpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"RPM Limit",children:o.rpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Session TPM Limit",children:o.session_tpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Session RPM Limit",children:o.session_rpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Created At",children:V(o.created_at)}),(0,t.jsx)(eK.Descriptions.Item,{label:"Updated At",children:V(o.updated_at)})]}),(0,t.jsx)(eZ,{keys:g,isLoading:h,onKeyClick:m}),o.object_permission&&(o.object_permission.mcp_servers?.length||o.object_permission.mcp_access_groups?.length||o.object_permission.mcp_tool_permissions&&Object.keys(o.object_permission.mcp_tool_permissions).length>0)&&(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eB.Title,{children:"MCP Tool Permissions"}),(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:[o.object_permission.mcp_servers&&o.object_permission.mcp_servers.length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"MCP Servers",children:o.object_permission.mcp_servers.join(", ")}),o.object_permission.mcp_access_groups&&o.object_permission.mcp_access_groups.length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"MCP Access Groups",children:o.object_permission.mcp_access_groups.join(", ")}),o.object_permission.mcp_tool_permissions&&Object.keys(o.object_permission.mcp_tool_permissions).length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"Tool permissions per server",children:(0,t.jsx)("div",{className:"space-y-1",children:Object.entries(o.object_permission.mcp_tool_permissions).map(([e,s])=>(0,t.jsxs)("div",{children:[(0,t.jsxs)("span",{className:"font-medium",children:[e,":"]})," ",Array.isArray(s)?s.join(", "):String(s)]},e))})})]})]}),(0,t.jsx)(e0,{agent:o}),o.agent_card_params?.skills&&o.agent_card_params.skills.length>0&&(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eB.Title,{children:"Skills"}),(0,t.jsx)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:o.agent_card_params.skills.map((e,s)=>(0,t.jsx)(eK.Descriptions.Item,{label:e.name||`Skill ${s+1}`,children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"ID:"})," ",e.id]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Description:"})," ",e.description]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Tags:"})," ",Array.isArray(e.tags)?e.tags.join(", "):e.tags]}),e.examples&&e.examples.length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Examples:"})," ",Array.isArray(e.examples)?e.examples.join(", "):e.examples]})]})},s))})]})]}),n&&(0,t.jsx)(ez.TabPanel,{children:(0,t.jsxs)(l.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(eB.Title,{children:"Agent Settings"}),!f&&(0,t.jsx)(a.Button,{onClick:()=>{P(null),k(!0)},children:"Edit Settings"})]}),f?(0,t.jsxs)(b.Form,{form:T,layout:"vertical",onFinish:q,children:[(0,t.jsx)(b.Form.Item,{label:"Agent ID",children:(0,t.jsx)(v.Input,{value:o.agent_id,disabled:!0})}),"a2a"===L?(0,t.jsx)(ed,{showAgentName:!0}):U?(0,t.jsx)(eD,{agentTypeInfo:U}):(0,t.jsx)(ed,{showAgentName:!0}),E&&(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eA,{accessToken:r,onApply:e=>{if(P(e),!e)return;let{selected_card:t}=e,s=(t.skills??[]).map(e=>({id:e.id??"",name:e.name??"",description:e.description??"",tags:e.tags??[],examples:e.examples??[]})),a={name:t.name,description:t.description,url:e.upstream_url,streaming:!!t.capabilities?.streaming,skills:s,iconUrl:t.iconUrl,documentationUrl:t.documentationUrl};for(let t of(U?.credential_fields??[]).map(e=>e.key).filter(e=>/(^|_)(url|api_base|endpoint)$/i.test(e)))a[t]=e.upstream_url;T.setFieldsValue(a)},discoveryRequest:E,savedAgentCard:o.agent_card_params??null})}),(0,t.jsx)(C.Divider,{}),(0,t.jsx)(eB.Title,{className:"mb-4",children:"Rate Limits"}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})})]}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"Session TPM Limit",name:"session_tpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})}),(0,t.jsx)(b.Form.Item,{label:"Session RPM Limit",name:"session_rpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})})]}),(0,t.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,t.jsx)(B.Button,{onClick:()=>{P(null),k(!1),R()},children:"Cancel"}),(0,t.jsx)(a.Button,{loading:N,children:"Save Changes"})]})]}):(0,t.jsx)(p.Text,{children:'Click "Edit Settings" to modify agent configuration.'})]})})]})]})]})};var e3=e.i(727749),e6=e.i(500330),e5=e.i(902555);let e7=({accessToken:e,userRole:b,teams:f})=>{let[v,k]=(0,s.useState)([]),[N,w]=(0,s.useState)(!1),[C,S]=(0,s.useState)(!1),[I,T]=(0,s.useState)(!1),[A,F]=(0,s.useState)(null),[L,D]=(0,s.useState)(null),[M,P]=(0,s.useState)(!1),R=!!b&&(0,eq.isAdminRole)(b),U=async t=>{if(e){S(!0);try{let s=await (0,y.getAgentsList)(e,t??M);k(s.agents||[])}catch(e){console.error("Error fetching agents:",e)}finally{S(!1)}}};(0,s.useEffect)(()=>{U()},[e]);let O=async()=>{if(A&&e){T(!0);try{await (0,y.deleteAgentCall)(e,A.id),e3.default.success(`Agent "${A.name}" deleted successfully`),U()}catch(e){console.error("Error deleting agent:",e),e3.default.fromBackend("Failed to delete agent")}finally{T(!1),F(null)}}},E=[...v].sort((e,t)=>{let s=e.created_at?new Date(e.created_at).getTime():0;return(t.created_at?new Date(t.created_at).getTime():0)-s}),q=R?7:6;return(0,t.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,t.jsxs)("div",{className:"flex flex-col gap-2 mb-4",children:[(0,t.jsx)("h1",{className:"text-2xl font-bold",children:"Agents"}),(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"List of A2A-spec agents that are available to be used in your organization. Go to AI Hub, to make agents public."}),(0,t.jsx)(h.Alert,{message:"Why do agents need keys?",description:"Keys scope access to an agent and allow it to call MCP tools. Assign a key when creating an agent or from the Virtual Keys page.",type:"info",showIcon:!0,className:"mb-3"}),(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-4",children:[R&&(0,t.jsx)(a.Button,{onClick:()=>{L&&D(null),w(!0)},disabled:!e,children:"+ Add New Agent"}),(0,t.jsx)(x.Tooltip,{title:"When enabled, only agents with reachable URLs are shown",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.CheckCircleOutlined,{className:M?"text-green-500":"text-gray-400"}),(0,t.jsx)("span",{className:"text-sm text-gray-600",children:"Health Check"}),(0,t.jsx)(_.Switch,{size:"small",checked:M,onChange:e=>{P(e),U(e)},loading:C&&M})]})})]})]}),L?(0,t.jsx)(e4,{agentId:L,onClose:()=>D(null),accessToken:e,isAdmin:R}):(0,t.jsx)(l.Card,{children:C?(0,t.jsx)(g.Skeleton,{active:!0,paragraph:{rows:3}}):(0,t.jsxs)(i.Table,{children:[(0,t.jsx)(o.TableHead,{children:(0,t.jsxs)(d.TableRow,{children:[(0,t.jsx)(c.TableHeaderCell,{children:"Agent Name"}),(0,t.jsx)(c.TableHeaderCell,{children:"Agent ID"}),(0,t.jsx)(c.TableHeaderCell,{children:"Spend (USD)"}),(0,t.jsx)(c.TableHeaderCell,{children:"Model"}),(0,t.jsx)(c.TableHeaderCell,{children:"Created"}),(0,t.jsx)(c.TableHeaderCell,{children:"Status"}),R&&(0,t.jsx)(c.TableHeaderCell,{children:"Actions"})]})}),(0,t.jsx)(r.TableBody,{children:0===E.length?(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(n.TableCell,{colSpan:q,children:(0,t.jsx)(p.Text,{className:"text-center",children:'No agents found. Click "+ Add New Agent" to create one.'})})}):E.map(e=>(0,t.jsxs)(d.TableRow,{children:[(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(p.Text,{children:e.agent_name})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(x.Tooltip,{title:e.agent_id,children:(0,t.jsxs)(a.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate max-w-[200px]",onClick:()=>D(e.agent_id),children:[e.agent_id.slice(0,7),"..."]})})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(p.Text,{children:(0,e6.formatNumberWithCommas)(e.spend,4)})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(m.Badge,{size:"xs",color:"blue",children:e.litellm_params?.model||"N/A"})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(p.Text,{children:e.created_at?new Date(e.created_at).toLocaleDateString():"N/A"})}),(0,t.jsx)(n.TableCell,{children:(e.keys?.length??0)>0?(0,t.jsx)(m.Badge,{color:"green",children:"Active"}):(0,t.jsx)(m.Badge,{color:"yellow",children:"Needs Setup"})}),R&&(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e5.default,{variant:"Delete",onClick:()=>{F({id:e.agent_id,name:e.agent_name})}})})]},e.agent_id))})]})}),(0,t.jsx)(eE,{visible:N,onClose:()=>{w(!1)},accessToken:e,onSuccess:()=>{U()},teams:f}),A&&(0,t.jsxs)(u.Modal,{title:"Delete Agent",open:null!==A,onOk:O,onCancel:()=>{F(null)},confirmLoading:I,okText:"Delete",okButtonProps:{danger:!0},children:[(0,t.jsxs)("p",{children:["Are you sure you want to delete agent: ",A.name,"?"]}),(0,t.jsx)("p",{children:"This action cannot be undone."})]})]})};var e9=e.i(785242);e.s(["default",0,function(){let{accessToken:e,userRole:s}=(0,R.default)(),{data:a}=(0,e9.useTeams)();return(0,t.jsx)(e7,{accessToken:e,userRole:s,teams:a??null})}],298805)}]); \ No newline at end of file +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,298805,e=>{"use strict";var t=e.i(843476),s=e.i(271645),a=e.i(994388),l=e.i(304967),i=e.i(269200),r=e.i(942232),n=e.i(977572),o=e.i(427612),c=e.i(64848),d=e.i(496020),m=e.i(389083),p=e.i(599724),u=e.i(212931),h=e.i(560445),x=e.i(592968),g=e.i(981339),_=e.i(790848),j=e.i(245704),y=e.i(602869),b=e.i(808613),f=e.i(199133),v=e.i(311451),k=e.i(280898),N=e.i(91739),w=e.i(262218),C=e.i(312361),S=e.i(28651),I=e.i(888259),T=e.i(555987),A=e.i(826910),F=e.i(438957),L=e.i(983561),D=e.i(477189),M=e.i(827252),P=e.i(364769),R=e.i(135214),U=e.i(355619),O=e.i(663435),E=e.i(362024),q=e.i(770914),V=e.i(464571),B=e.i(646563),$=e.i(564897);let H={basic:{key:"basic",title:"Basic Information",defaultExpanded:!0,fields:[{name:"name",label:"Display Name",type:"text",required:!0,placeholder:"e.g., Customer Support Agent"},{name:"description",label:"Description",type:"textarea",required:!0,placeholder:"Describe what this agent does...",rows:3},{name:"url",label:"URL",type:"url",required:!1,placeholder:"http://localhost:9999/",tooltip:"Base URL where the agent is hosted (optional)"},{name:"version",label:"Version",type:"text",placeholder:"1.0.0",defaultValue:"1.0.0"},{name:"protocolVersion",label:"Protocol Version",type:"select",options:["1.0","0.3"],defaultValue:"1.0",tooltip:"The A2A protocol version LiteLLM serves to clients for this agent. LiteLLM converts the upstream agent's responses to this version, so clients always see the version you pick here regardless of the original agent's version.",helpText:"LiteLLM serves this version to clients and converts the upstream agent's responses to match it, regardless of the original agent's version."}]},skills:{key:"skills",title:"Skills",fields:[{name:"skills",label:"Skills",type:"list",defaultValue:[]}]},capabilities:{key:"capabilities",title:"Capabilities",fields:[{name:"streaming",label:"Streaming",type:"switch",defaultValue:!1},{name:"pushNotifications",label:"Push Notifications",type:"switch"},{name:"stateTransitionHistory",label:"State Transition History",type:"switch"}]},optional:{key:"optional",title:"Optional Settings",fields:[{name:"iconUrl",label:"Icon URL",type:"url",placeholder:"https://example.com/icon.png"},{name:"documentationUrl",label:"Documentation URL",type:"url",placeholder:"https://docs.example.com"},{name:"supportsAuthenticatedExtendedCard",label:"Supports Authenticated Extended Card",type:"switch"}]},litellm:{key:"litellm",title:"LiteLLM Parameters",fields:[{name:"model",label:"Model (Optional)",type:"text"},{name:"make_public",label:"Make Public",type:"switch"}]},cost:{key:"cost",title:"Cost Configuration",fields:[{name:"cost_per_query",label:"Cost Per Query ($)",type:"text",placeholder:"0.0",tooltip:"Fixed cost per query"},{name:"input_cost_per_token",label:"Input Cost Per Token ($)",type:"text",placeholder:"0.000001",tooltip:"Cost per input token"},{name:"output_cost_per_token",label:"Output Cost Per Token ($)",type:"text",placeholder:"0.000002",tooltip:"Cost per output token"}]},tracing:{key:"tracing",title:"Tracing",fields:[{name:"enable_tracing",label:"Enable Tracing",type:"switch",defaultValue:!1,tooltip:"Enable request tracing for this agent"}]}},z="Skill ID",G=!0,K="e.g., hello_world",W="Skill Name",Y=!0,J="e.g., Returns hello world",Q="Description",X=!0,Z="What this skill does",ee=2,et="Tags",es=!0,ea="Type a tag and press Enter",el="Examples",ei="Type an example and press Enter",er=(e,t)=>{let s={agent_name:e.agent_name,agent_card_params:{protocolVersion:e.protocolVersion||"1.0",name:e.name||e.agent_name,description:e.description||"",url:e.url||"",version:e.version||"1.0.0",defaultInputModes:t?.agent_card_params?.defaultInputModes||["text"],defaultOutputModes:t?.agent_card_params?.defaultOutputModes||["text"],capabilities:{streaming:!0===e.streaming,...void 0!==e.pushNotifications&&{pushNotifications:e.pushNotifications},...void 0!==e.stateTransitionHistory&&{stateTransitionHistory:e.stateTransitionHistory}},skills:e.skills||[],...e.iconUrl&&{iconUrl:e.iconUrl},...e.documentationUrl&&{documentationUrl:e.documentationUrl},...void 0!==e.supportsAuthenticatedExtendedCard&&{supportsAuthenticatedExtendedCard:e.supportsAuthenticatedExtendedCard}}},a={};if(e.model&&(a.model=e.model),void 0!==e.make_public&&(a.make_public=e.make_public),e.cost_per_query&&(a.cost_per_query=parseFloat(e.cost_per_query)),e.input_cost_per_token&&(a.input_cost_per_token=parseFloat(e.input_cost_per_token)),e.output_cost_per_token&&(a.output_cost_per_token=parseFloat(e.output_cost_per_token)),Object.keys(a).length>0&&(s.litellm_params=a),null!=e.tpm_limit&&(s.tpm_limit=e.tpm_limit),null!=e.rpm_limit&&(s.rpm_limit=e.rpm_limit),null!=e.session_tpm_limit&&(s.session_tpm_limit=e.session_tpm_limit),null!=e.session_rpm_limit&&(s.session_rpm_limit=e.session_rpm_limit),Array.isArray(e.static_headers)&&e.static_headers.length>0){let t={};e.static_headers.forEach(e=>{let s=e?.header?.trim();s&&(t[s]=e?.value??"")}),Object.keys(t).length>0&&(s.static_headers=t)}return Array.isArray(e.extra_headers)&&e.extra_headers.length>0&&(s.extra_headers=e.extra_headers),s},en=e=>{let t=e.agent_card_params?.skills?.map(e=>({...e,tags:e.tags,examples:e.examples||[]}))||[];return{agent_name:e.agent_name,name:e.agent_card_params?.name,description:e.agent_card_params?.description,url:e.agent_card_params?.url,version:e.agent_card_params?.version,protocolVersion:e.agent_card_params?.protocolVersion,streaming:e.agent_card_params?.capabilities?.streaming,pushNotifications:e.agent_card_params?.capabilities?.pushNotifications,stateTransitionHistory:e.agent_card_params?.capabilities?.stateTransitionHistory,skills:t,iconUrl:e.agent_card_params?.iconUrl,documentationUrl:e.agent_card_params?.documentationUrl,supportsAuthenticatedExtendedCard:e.agent_card_params?.supportsAuthenticatedExtendedCard,model:e.litellm_params?.model,make_public:e.litellm_params?.make_public,cost_per_query:e.litellm_params?.cost_per_query,input_cost_per_token:e.litellm_params?.input_cost_per_token,output_cost_per_token:e.litellm_params?.output_cost_per_token,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,session_tpm_limit:e.session_tpm_limit,session_rpm_limit:e.session_rpm_limit,static_headers:e.static_headers?Object.entries(e.static_headers).map(([e,t])=>({header:e,value:t})):[],extra_headers:e.extra_headers??[]}},eo=()=>(0,t.jsx)(t.Fragment,{children:H.cost.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,tooltip:e.tooltip,children:(0,t.jsx)(v.Input,{placeholder:e.placeholder,type:"number",step:"0.000001"})},e.name))}),{Panel:ec}=E.Collapse,ed=({showAgentName:e=!0,visiblePanels:s})=>{let a=e=>!s||s.includes(e);return(0,t.jsxs)(t.Fragment,{children:[e&&(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter a unique agent name"}],tooltip:"Unique identifier for the agent",children:(0,t.jsx)(v.Input,{placeholder:"e.g., customer-support-agent"})}),(0,t.jsxs)(E.Collapse,{defaultActiveKey:["basic"],style:{marginBottom:16},children:[a(H.basic.key)&&(0,t.jsx)(ec,{header:`${H.basic.title} (Required)`,children:H.basic.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,rules:e.required?[{required:!0,message:`Please enter ${e.label.toLowerCase()}`}]:void 0,tooltip:e.tooltip,extra:e.helpText,children:"textarea"===e.type?(0,t.jsx)(v.Input.TextArea,{rows:e.rows,placeholder:e.placeholder}):"select"===e.type?(0,t.jsx)(f.Select,{placeholder:e.placeholder,children:(e.options??[]).map(e=>(0,t.jsx)(f.Select.Option,{value:e,children:e},e))}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.basic.key),a(H.skills.key)&&(0,t.jsx)(ec,{header:`${H.skills.title}`,children:(0,t.jsx)(b.Form.List,{name:"skills",children:(e,{add:s,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(e=>(0,t.jsxs)("div",{style:{marginBottom:16,padding:16,border:"1px solid #d9d9d9",borderRadius:4},children:[(0,t.jsx)(b.Form.Item,{...e,label:z,name:[e.name,"id"],rules:[{required:G,message:"Required"}],children:(0,t.jsx)(v.Input,{placeholder:K})}),(0,t.jsx)(b.Form.Item,{...e,label:W,name:[e.name,"name"],rules:[{required:Y,message:"Required"}],children:(0,t.jsx)(v.Input,{placeholder:J})}),(0,t.jsx)(b.Form.Item,{...e,label:Q,name:[e.name,"description"],rules:[{required:X,message:"Required"}],children:(0,t.jsx)(v.Input.TextArea,{rows:ee,placeholder:Z})}),(0,t.jsx)(b.Form.Item,{...e,label:et,name:[e.name,"tags"],rules:[{required:es,message:"Required"}],children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},tokenSeparators:[","],placeholder:ea})}),(0,t.jsx)(b.Form.Item,{...e,label:el,name:[e.name,"examples"],children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},tokenSeparators:[","],placeholder:ei})}),(0,t.jsx)(V.Button,{type:"link",danger:!0,onClick:()=>a(e.name),icon:(0,t.jsx)($.MinusCircleOutlined,{}),children:"Remove Skill"})]},e.key)),(0,t.jsx)(V.Button,{type:"dashed",onClick:()=>s(),icon:(0,t.jsx)(B.PlusOutlined,{}),style:{width:"100%"},children:"Add Skill"})]})})},H.skills.key),a(H.capabilities.key)&&(0,t.jsx)(ec,{header:H.capabilities.title,children:H.capabilities.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"checked",children:(0,t.jsx)(_.Switch,{})},e.name))},H.capabilities.key),a(H.optional.key)&&(0,t.jsx)(ec,{header:H.optional.title,children:H.optional.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"switch"===e.type?"checked":void 0,children:"switch"===e.type?(0,t.jsx)(_.Switch,{}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.optional.key),a(H.cost.key)&&(0,t.jsx)(ec,{header:H.cost.title,children:(0,t.jsx)(eo,{})},H.cost.key),a(H.litellm.key)&&(0,t.jsx)(ec,{header:H.litellm.title,children:H.litellm.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"switch"===e.type?"checked":void 0,children:"switch"===e.type?(0,t.jsx)(_.Switch,{}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.litellm.key),a("auth_headers")&&(0,t.jsxs)(ec,{header:"Authentication Headers",children:[(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Static Headers"," ",(0,t.jsx)(x.Tooltip,{title:"Headers always sent to the backend agent, regardless of the client request. Admin-configured, static wins on conflict.",children:(0,t.jsx)(M.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),children:(0,t.jsx)(b.Form.List,{name:"static_headers",children:(e,{add:s,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(({key:e,name:s,...l})=>(0,t.jsxs)(q.Space,{style:{display:"flex",marginBottom:8},align:"baseline",children:[(0,t.jsx)(b.Form.Item,{...l,name:[s,"header"],rules:[{required:!0,message:"Header name required"}],children:(0,t.jsx)(v.Input,{placeholder:"Header name (e.g. Authorization)",style:{width:220}})}),(0,t.jsx)(b.Form.Item,{...l,name:[s,"value"],rules:[{required:!0,message:"Value required"}],children:(0,t.jsx)(v.Input,{placeholder:"Value (e.g. Bearer token123)",style:{width:260}})}),(0,t.jsx)($.MinusCircleOutlined,{onClick:()=>a(s),style:{color:"#ff4d4f"}})]},e)),(0,t.jsx)(V.Button,{type:"dashed",onClick:()=>s(),icon:(0,t.jsx)(B.PlusOutlined,{}),style:{width:"100%"},children:"Add Static Header"})]})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Forward Client Headers"," ",(0,t.jsx)(x.Tooltip,{title:"Header names to extract from the client's request and forward to the agent. Type a name and press Enter.",children:(0,t.jsx)(M.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),name:"extra_headers",children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},placeholder:"e.g. x-api-key, Authorization",tokenSeparators:[","]})})]},"auth_headers")]})]})};var em=e.i(536916),ep=e.i(21548),eu=e.i(482725),eh=e.i(898586);e.i(247167);var ex=e.i(931067);let eg={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z",fill:e}},{tag:"path",attrs:{d:"M512 140c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm193.4 225.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.3 0 19.9 5 25.9 13.3l71.2 98.8 157.2-218c6-8.4 15.7-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.4 12.7z",fill:t}},{tag:"path",attrs:{d:"M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z",fill:e}}]}},name:"check-circle",theme:"twotone"};var e_=e.i(9583),ej=s.forwardRef(function(e,t){return s.createElement(e_.default,(0,ex.default)({},e,{ref:t,icon:eg}))}),ey=e.i(596239),eb=e.i(91979),ef=e.i(928685);let ev=(e,t)=>e?.id??e?.name??`skill-${t}`,ek=["streaming"],eN=e=>e?ek.reduce((t,s)=>(s in e&&(t[s]=!!e[s]),t),{}):{},ew=(e,t)=>t?{...e,agent_card_params:{...e.agent_card_params,name:t.name??e.agent_card_params?.name,description:t.description??e.agent_card_params?.description,...Array.isArray(t.skills)&&{skills:t.skills},...t.capabilities&&{capabilities:t.capabilities},...Array.isArray(t.defaultInputModes)&&t.defaultInputModes.length>0&&{defaultInputModes:t.defaultInputModes},...Array.isArray(t.defaultOutputModes)&&t.defaultOutputModes.length>0&&{defaultOutputModes:t.defaultOutputModes},...t.provider&&{provider:t.provider},...t.iconUrl&&{iconUrl:t.iconUrl},...t.documentationUrl&&{documentationUrl:t.documentationUrl}}}:e,eC=(e,t,s)=>{let a=e=>(e??"").toString().trim();if("langgraph"===e){let e=a(t.api_base).replace(/\/+$/,""),s=a(t.assistant_id);if(!e||!s)return;let l=`?assistant_id=${encodeURIComponent(s)}`;return{url:e,discovery_mode:"langgraph_platform",params:{assistant_id:s},display_url:`${e}/.well-known/agent-card.json${l}`}}if("a2a"===e||s?.use_a2a_form_fields){let e=a(t.url).replace(/\/+$/,"");if(!e)return;return{url:e,discovery_mode:"well_known_fallback",display_url:`${e}/.well-known/agent-card.json`}}},{Text:eS,Paragraph:eI}=eh.Typography,{Panel:eT}=E.Collapse,eA=({accessToken:e,onApply:a,discoveryRequest:l,savedAgentCard:i})=>{let[r,n]=(0,s.useState)(""),[o,c]=(0,s.useState)(!1),[d,m]=(0,s.useState)(null),[p,u]=(0,s.useState)(null),g=void 0!==l,j=g?l.url:r,[b,f]=(0,s.useState)(""),[k,N]=(0,s.useState)(""),[C,S]=(0,s.useState)(new Set),[I,T]=(0,s.useState)({}),A=(0,s.useRef)(a);A.current=a;let F=(0,s.useRef)(0),L=(0,s.useRef)(null),D=(0,s.useRef)(l);D.current=l;let P=(0,s.useRef)(i);P.current=i;let R=l?.discovery_mode,U=(0,s.useMemo)(()=>JSON.stringify(l?.params??null),[l?.params]),O=(0,s.useCallback)(async()=>{if(!e){m("No access token available"),A.current(null);return}let t=j.trim();if(!t){m(g?"Fill in the agent's connection details above first":"Enter the agent's base URL first"),u(null),A.current(null);return}let s=D.current,a=++F.current;c(!0),m(null);try{var l;let i,r,n,o=await (0,y.discoverAgentCardCall)(e,t,g&&s?{discovery_mode:s.discovery_mode,params:s.params}:void 0);if(a!==F.current)return;L.current=null,u(o.agent_card),l=o.agent_card,n=(i=P.current)?((e,t)=>{let s=e.skills??[],a=t?.skills??[],l=new Set(a.map(e=>e?.id).filter(Boolean)),i=new Set(a.map(e=>e?.name).filter(Boolean)),r=new Set;s.forEach((e,t)=>{let s=ev(e,t),a=e.id&&l.has(e.id),n=e.name&&i.has(e.name);(a||n)&&r.add(s)});let n=eN(e.capabilities);if(t?.capabilities)for(let e of ek)e in t.capabilities&&(n[e]=!!t.capabilities[e]);return{editedName:t?.name??e.name??"",editedDescription:t?.description??e.description??"",selectedSkillIds:r,selectedCapabilities:n}})(l,i):(r=l.skills??[],{editedName:l.name??"",editedDescription:l.description??"",selectedSkillIds:new Set(r.map((e,t)=>ev(e,t))),selectedCapabilities:eN(l.capabilities)}),f(n.editedName),N(n.editedDescription),S(n.selectedSkillIds),T(n.selectedCapabilities)}catch(e){if(a!==F.current)return;m(e?.message?String(e.message):"Failed to discover agent card"),u(null),L.current=null,A.current(null)}finally{a===F.current&&c(!1)}},[e,j,g,R,U]);(0,s.useEffect)(()=>{if(!e)return;if(!j.trim()){u(null),m(null),L.current=null,A.current(null);return}let t=window.setTimeout(()=>{O()},400);return()=>window.clearTimeout(t)},[e,j,O]);let B=(0,s.useCallback)(()=>{if(!p)return null;let e=(p.skills??[]).filter((e,t)=>C.has(ev(e,t))),t={...p,name:b,description:k,skills:e,capabilities:{...I}};return{raw_card:p,selected_card:t,upstream_url:j.trim()}},[p,k,b,j,I,C]);(0,s.useEffect)(()=>{if(!p)return;let e=B(),t=JSON.stringify(e);L.current!==t&&(L.current=t,A.current(e))},[B,p]);let $=p?.skills?.length??0,H=C.size;return(0,t.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4 bg-gray-50 mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-2",children:[(0,t.jsx)(ey.LinkOutlined,{className:"text-indigo-600"}),(0,t.jsx)(eS,{strong:!0,children:"Discover from agent URL"}),(0,t.jsx)(x.Tooltip,{title:"LiteLLM will fetch /.well-known/agent-card.json from this URL and let you pick which skills and capabilities to expose through the proxy.",children:(0,t.jsx)(M.InfoCircleOutlined,{className:"text-gray-400"})})]}),g?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eI,{className:"text-xs text-gray-500 mb-2",children:"Using the connection details you entered above. We'll fetch:"}),(0,t.jsx)("div",{className:"bg-white border border-gray-200 rounded-sm px-3 py-2 mb-3 font-mono text-xs text-gray-700 break-all",children:l.display_url||j||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Fill in the fields above first"})}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(V.Button,{type:"primary",icon:p?(0,t.jsx)(eb.ReloadOutlined,{}):(0,t.jsx)(ef.SearchOutlined,{}),loading:o,onClick:O,disabled:!j.trim(),children:p?"Re-discover":"Discover"})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(eI,{className:"text-xs text-gray-500 mb-3",children:["Paste the upstream agent's base URL. We'll try ",(0,t.jsx)("code",{children:"/.well-known/agent-card.json"}),","," ",(0,t.jsx)("code",{children:"/.well-known/agent.json"}),", and ",(0,t.jsx)("code",{children:"/agent.json"})," in order."]}),(0,t.jsxs)(q.Space.Compact,{style:{width:"100%"},children:[(0,t.jsx)(v.Input,{placeholder:"https://upstream-agent.example.com",value:r,onChange:e=>n(e.target.value),onPressEnter:O,allowClear:!0,disabled:o}),(0,t.jsx)(V.Button,{type:"primary",icon:p?(0,t.jsx)(eb.ReloadOutlined,{}):(0,t.jsx)(ef.SearchOutlined,{}),loading:o,onClick:O,children:p?"Re-discover":"Discover"})]})]}),d&&(0,t.jsx)(h.Alert,{className:"mt-3",type:"error",message:"Discovery failed",description:d,showIcon:!0,closable:!0,onClose:()=>m(null)}),o&&!p&&(0,t.jsx)("div",{className:"flex items-center justify-center py-8",children:(0,t.jsx)(eu.Spin,{})}),p&&(0,t.jsxs)("div",{className:"mt-4 bg-white border border-gray-200 rounded-lg p-4",children:[(0,t.jsx)("div",{className:"flex items-center justify-between mb-3",children:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(ej,{twoToneColor:"#52c41a"}),(0,t.jsx)(eS,{strong:!0,children:"Upstream card loaded"}),p.version&&(0,t.jsxs)(w.Tag,{color:"blue",children:["v",p.version]}),p.provider?.organization&&(0,t.jsx)(w.Tag,{color:"purple",children:p.provider.organization})]})}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3 mb-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-600 block mb-1",children:"Name (shown to API clients)"}),(0,t.jsx)(v.Input,{value:b,onChange:e=>f(e.target.value),placeholder:"Agent name"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-600 block mb-1",children:"Description"}),(0,t.jsx)(v.Input.TextArea,{value:k,onChange:e=>N(e.target.value),rows:2,placeholder:"What this agent does"})]})]}),(0,t.jsxs)(E.Collapse,{defaultActiveKey:["skills","capabilities"],ghost:!0,className:"bg-transparent",children:[(0,t.jsx)(eT,{header:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(eS,{strong:!0,children:"Skills"}),(0,t.jsxs)(w.Tag,{children:[H," / ",$," selected"]})]}),children:0===$?(0,t.jsx)(ep.Empty,{image:ep.Empty.PRESENTED_IMAGE_SIMPLE,description:"Upstream card has no skills"}):(0,t.jsx)("div",{className:"space-y-2",children:(p.skills??[]).map((e,s)=>{let a=ev(e,s),l=C.has(a);return(0,t.jsxs)("label",{className:`flex items-start gap-3 p-3 border rounded cursor-pointer transition-colors ${l?"border-indigo-300 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,children:[(0,t.jsx)(em.Checkbox,{checked:l,onChange:e=>{var t;return t=e.target.checked,void S(e=>{let s=new Set(e);return t?s.add(a):s.delete(a),s})}}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[(0,t.jsx)(eS,{strong:!0,children:e.name||a}),e.id&&(0,t.jsx)(w.Tag,{style:{marginLeft:0},children:e.id}),(e.tags??[]).map(e=>(0,t.jsx)(w.Tag,{color:"geekblue",children:e},e))]}),e.description&&(0,t.jsx)(eI,{className:"text-xs text-gray-500 mt-1 mb-0",ellipsis:{rows:2,expandable:!0,symbol:"more"},children:e.description})]})]},a)})})},"skills"),(0,t.jsx)(eT,{header:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(eS,{strong:!0,children:"Capabilities"}),(0,t.jsx)(x.Tooltip,{title:"Only capabilities LiteLLM can faithfully proxy today are listed. Others (push notifications, extensions) are coming soon.",children:(0,t.jsx)(M.InfoCircleOutlined,{className:"text-gray-400"})})]}),children:(0,t.jsx)("div",{className:"space-y-2",children:ek.map(e=>{let s=!!p.capabilities?.[e];return(0,t.jsxs)("div",{className:"flex items-center justify-between p-2 border border-gray-200 rounded-sm bg-white",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(eS,{strong:!0,className:"capitalize",children:e}),!s&&(0,t.jsx)(w.Tag,{className:"ml-2",color:"default",children:"not advertised upstream"})]}),(0,t.jsx)(_.Switch,{checked:!!I[e],onChange:t=>T(s=>({...s,[e]:t}))})]},e)})})},"capabilities")]})]})]})},{Panel:eF}=E.Collapse,eL=(e,t)=>{let s={...t.litellm_params_template||{}};for(let a of t.credential_fields){let t=e[a.key];t&&!1!==a.include_in_litellm_params&&(s[a.key]=t)}if(e.cost_per_query&&(s.cost_per_query=parseFloat(e.cost_per_query)),e.input_cost_per_token&&(s.input_cost_per_token=parseFloat(e.input_cost_per_token)),e.output_cost_per_token&&(s.output_cost_per_token=parseFloat(e.output_cost_per_token)),t.model_template){let a=t.model_template;for(let s of t.credential_fields){let t=`{${s.key}}`;a.includes(t)&&e[s.key]&&(a=a.replace(t,e[s.key]))}s.model=a}let a={agent_name:e.agent_name,agent_card_params:{protocolVersion:"1.0",name:e.display_name||e.agent_name,description:e.description||`${t.agent_type_display_name} agent`,url:e.api_base||"",version:"1.0.0",defaultInputModes:["text"],defaultOutputModes:["text"],capabilities:{streaming:!0},skills:[{id:"chat",name:"Chat",description:"General chat capability",tags:["chat","conversation"]}]},litellm_params:s};return null!=e.tpm_limit&&(a.tpm_limit=e.tpm_limit),null!=e.rpm_limit&&(a.rpm_limit=e.rpm_limit),null!=e.session_tpm_limit&&(a.session_tpm_limit=e.session_tpm_limit),null!=e.session_rpm_limit&&(a.session_rpm_limit=e.session_rpm_limit),a},eD=({agentTypeInfo:e})=>(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter a unique agent name"}],tooltip:"Unique identifier for the agent",children:(0,t.jsx)(v.Input,{placeholder:"e.g., my-langgraph-agent"})}),(0,t.jsx)(b.Form.Item,{label:"Description",name:"description",tooltip:"Brief description of what this agent does",children:(0,t.jsx)(v.Input.TextArea,{rows:2,placeholder:"Describe what this agent does..."})}),e.credential_fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:`Please enter ${e.label}`}]:void 0,tooltip:e.tooltip,initialValue:e.default_value,children:"password"===e.field_type?(0,t.jsx)(v.Input.Password,{placeholder:e.placeholder||""}):"textarea"===e.field_type?(0,t.jsx)(v.Input.TextArea,{rows:3,placeholder:e.placeholder||""}):"select"===e.field_type&&e.options?(0,t.jsx)(f.Select,{placeholder:e.placeholder||"",children:e.options.map(e=>(0,t.jsx)(f.Select.Option,{value:e,children:e},e))}):(0,t.jsx)(v.Input,{placeholder:e.placeholder||""})},e.key)),(0,t.jsx)(E.Collapse,{style:{marginBottom:16},children:(0,t.jsx)(eF,{header:H.cost.title,children:(0,t.jsx)(eo,{})},H.cost.key)})]});var eM=e.i(75921),eP=e.i(390605),eR=e.i(891547);let{Step:eU}=k.Steps,eO="custom",eE=({visible:e,onClose:l,accessToken:i,onSuccess:r,teams:n})=>{let o,c,{userId:d,userRole:m}=(0,R.default)(),[p]=b.Form.useForm(),[h,x]=(0,s.useState)(0),[g,j]=(0,s.useState)(!1),[E,q]=(0,s.useState)("a2a"),[V,B]=(0,s.useState)([]),[$,z]=(0,s.useState)(!1),[G,K]=(0,s.useState)("create_new"),[W,Y]=(0,s.useState)(""),[J,Q]=(0,s.useState)([]),[X,Z]=(0,s.useState)([]),[ee,et]=(0,s.useState)(null),[es,ea]=(0,s.useState)(!1),[el,ei]=(0,s.useState)([]),[en,eo]=(0,s.useState)(!1),[ec,em]=(0,s.useState)([]),[ep,eu]=(0,s.useState)(!1),[eh,ex]=(0,s.useState)(""),[eg,e_]=(0,s.useState)(null),[ej,ey]=(0,s.useState)(null),[eb,ef]=(0,s.useState)(!1),[ev,ek]=(0,s.useState)(!1),[eN,eS]=(0,s.useState)(null),[eI,eT]=(0,s.useState)(null),[eF,eE]=(0,s.useState)(null);(0,s.useEffect)(()=>{(async()=>{z(!0);try{let e=await (0,y.getAgentCreateMetadata)();B(e)}catch(e){console.error("Error fetching agent metadata:",e)}finally{z(!1)}})()},[]),(0,s.useEffect)(()=>{3===h&&i&&0===X.length&&(async()=>{ea(!0);try{let e=await (0,y.keyListCall)(i,null,null,null,null,null,1,100);Z(e?.keys||[])}catch(e){console.error("Error fetching keys:",e)}finally{ea(!1)}})()},[h,i]),(0,s.useEffect)(()=>{if(1!==h&&3!==h||!i||!d||!m)return;let e=!1;return eo(!0),(0,y.modelAvailableCall)(i,d,m).then(t=>{e||ei((t?.data??(Array.isArray(t)?t:[])).map(e=>e.id??e.model_name).filter(Boolean))}).catch(t=>{e||console.error("Error fetching models:",t)}).finally(()=>{e||eo(!1)}),()=>{e=!0}},[h,i,d,m]),(0,s.useEffect)(()=>{if(1!==h||!i)return;let e=!1;return eu(!0),(0,y.getAgentsList)(i).then(t=>{e||em((t?.agents??[]).map(e=>({agent_id:e.agent_id,agent_name:e.agent_name})))}).catch(t=>{e||console.error("Error fetching agents:",t)}).finally(()=>{e||eu(!1)}),()=>{e=!0}},[h,i]);let eq=V.find(e=>e.agent_type===E),eV=b.Form.useWatch([],p),eB=s.default.useMemo(()=>eC(E,eV||{},eq),[eV,eq,E]),e$=async()=>{try{if(0===h){await p.validateFields();let e=p.getFieldValue("agent_name");e&&!W&&Y(`${e}-key`)}x(e=>e+1)}catch{}},eH=async()=>{if(!i)return void I.default.error("No access token available");j(!0);try{await p.validateFields();let e={...p.getFieldsValue(!0)},t=(e=>{let t;if(E===eO)return{agent_name:e.agent_name,agent_card_params:{protocolVersion:"1.0",name:e.agent_name,description:e.description||"",url:"",version:"1.0.0",defaultInputModes:["text"],defaultOutputModes:["text"],capabilities:{streaming:!1},skills:[]}};if("a2a"===E)t=er(e);else if(eq?.use_a2a_form_fields)for(let s of(t=er(e),eq.litellm_params_template&&(t.litellm_params={...t.litellm_params,...eq.litellm_params_template}),eq.credential_fields)){let a=e[s.key];a&&!1!==s.include_in_litellm_params&&(t.litellm_params[s.key]=a)}else{if(!eq)return null;t=eL(e,eq)}return ew(t,eF?.selected_card)})(e);if(!t){I.default.error("Failed to build agent data"),j(!1);return}let s=e.allowed_mcp_servers_and_groups,a=e.mcp_tool_permissions||{},l=e.entitlement_models||[],n=e.entitlement_agents||[];(s?.servers?.length>0||s?.accessGroups?.length>0||Object.keys(a).length>0||l.length>0||n.length>0)&&(t.object_permission={},s?.servers?.length>0&&(t.object_permission.mcp_servers=s.servers),s?.accessGroups?.length>0&&(t.object_permission.mcp_access_groups=s.accessGroups),Object.keys(a).length>0&&(t.object_permission.mcp_tool_permissions=a),l.length>0&&(t.object_permission.models=l),n.length>0&&(t.object_permission.agents=n)),(eb||ev)&&(t.litellm_params||(t.litellm_params={}),eb&&(t.litellm_params.require_trace_id_on_calls_to_agent=!0),ev&&(t.litellm_params.require_trace_id_on_calls_by_agent=!0,eN&&(t.litellm_params.max_iterations=eN),eI&&(t.litellm_params.max_budget_per_session=eI)));let o=e.guardrails||[];o.length>0&&(t.litellm_params||(t.litellm_params={}),t.litellm_params.guardrails=o);let c=e.team_id||null;c&&(t.team_id=c);let d=await (0,y.createAgentCall)(i,t),m=d.agent_id,u=d.agent_name||e.agent_name||m;if(ex(u),"create_new"===G&&W){let e=await (0,y.keyCreateForAgentCall)(i,m,W,J,void 0,c);e_(e.key||null)}else if("existing_key"===G){if(!ee){I.default.error("Please select an existing key to assign"),j(!1);return}await (0,y.keyUpdateCall)(i,{key:ee,agent_id:m});let e=X.find(e=>e.token===ee);ey(e?.key_alias||ee.slice(0,12)+"…")}x(4),r()}catch(t){console.error("Error creating agent:",t);let e=t instanceof Error?t.message:String(t);I.default.error(e?`Failed to create agent: ${e}`:"Failed to create agent")}finally{j(!1)}},ez=()=>{p.resetFields(),q("a2a"),x(0),K("create_new"),Y(""),Q([]),et(null),ex(""),e_(null),ey(null),ef(!1),ek(!1),eS(null),eT(null),eE(null),l()},eG=e=>{q(e),p.resetFields(),eE(null)},eK=E===eO?null:eq?.logo_url||V.find(e=>"a2a"===e.agent_type)?.logo_url;return(0,t.jsx)(u.Modal,{title:(0,t.jsxs)("div",{className:"flex items-center space-x-3 pb-4 border-b border-gray-100",children:[eK&&h<1&&(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(eK),alt:"Agent",className:"w-6 h-6 object-contain"}),(0,t.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:"Add New Agent"})]}),open:e,onCancel:ez,footer:null,width:900,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)(k.Steps,{current:h,size:"small",className:"mb-8",children:[(0,t.jsx)(eU,{title:"Configure"}),(0,t.jsx)(eU,{title:"Entitlements"}),(0,t.jsx)(eU,{title:"Governance"}),(0,t.jsx)(eU,{title:"Agent Management"}),(0,t.jsx)(eU,{title:"Ready"})]}),(0,t.jsxs)(b.Form,{form:p,layout:"vertical",initialValues:"a2a"===E?{...(o={defaultInputModes:["text"],defaultOutputModes:["text"]},Object.values(H).forEach(e=>{e.fields.forEach(e=>{void 0!==e.defaultValue&&(o[e.name]=e.defaultValue)})}),o),allowed_mcp_servers_and_groups:{servers:[],accessGroups:[]},mcp_tool_permissions:{},entitlement_models:[],entitlement_agents:[],guardrails:[]}:{allowed_mcp_servers_and_groups:{servers:[],accessGroups:[]},mcp_tool_permissions:{},entitlement_models:[],entitlement_agents:[],guardrails:[]},className:"space-y-4",children:[0===h&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Agent Type"}),required:!0,tooltip:"Select the type of agent you want to create",children:(0,t.jsx)(f.Select,{value:E,onChange:eG,size:"large",style:{width:"100%"},optionLabelProp:"label",dropdownRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,(0,t.jsx)(C.Divider,{style:{margin:"4px 0"}}),(0,t.jsxs)("div",{className:"px-2 py-1",children:[(0,t.jsx)("div",{className:"text-xs text-gray-400 font-medium mb-1 uppercase tracking-wide px-2",children:"Not listed?"}),(0,t.jsxs)("div",{className:`flex items-center gap-3 px-2 py-2 rounded cursor-pointer transition-colors ${E===eO?"bg-amber-50":"hover:bg-amber-50"}`,onClick:()=>eG(eO),children:[(0,t.jsx)(D.AppstoreOutlined,{className:"text-amber-600 text-lg"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"font-medium text-amber-700",children:"Custom / Other"}),(0,t.jsx)(w.Tag,{color:"orange",style:{fontSize:10,padding:"0 4px"},children:"GENERIC"})]}),(0,t.jsx)("div",{className:"text-xs text-amber-600",children:"For agents that don't follow a standard protocol — just needs a virtual key"})]})]})]})]}),children:V.map(e=>(0,t.jsx)(f.Select.Option,{value:e.agent_type,label:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(e.logo_url)??"",alt:"",className:"w-4 h-4 object-contain"}),(0,t.jsx)("span",{children:e.agent_type_display_name})]}),children:(0,t.jsxs)("div",{className:"flex items-center gap-3 py-1",children:[(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(e.logo_url)??"",alt:e.agent_type_display_name,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"font-medium",children:e.agent_type_display_name}),e.description&&(0,t.jsx)("div",{className:"text-xs text-gray-500",children:e.description})]})]})},e.agent_type))})}),(0,t.jsxs)("div",{className:"mt-4",children:[E===eO?(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter an agent name"}],children:(0,t.jsx)(v.Input,{placeholder:"e.g. my-custom-agent"})}),(0,t.jsx)(b.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(v.Input.TextArea,{placeholder:"Describe what this agent does…",rows:3})})]}):"a2a"===E?(0,t.jsx)(ed,{showAgentName:!0}):eq?.use_a2a_form_fields?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(ed,{showAgentName:!0}),eq.credential_fields.length>0&&(0,t.jsxs)("div",{className:"mt-4 p-4 border border-gray-200 rounded-lg",children:[(0,t.jsxs)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:[eq.agent_type_display_name," Settings"]}),eq.credential_fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:`Please enter ${e.label}`}]:void 0,tooltip:e.tooltip,initialValue:e.default_value,children:"password"===e.field_type?(0,t.jsx)(v.Input.Password,{placeholder:e.placeholder||""}):(0,t.jsx)(v.Input,{placeholder:e.placeholder||""})},e.key))]})]}):eq?(0,t.jsx)(eD,{agentTypeInfo:eq}):null,E!==eO&&(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eA,{accessToken:i,onApply:e=>{if(eE(e),!e)return;let{selected_card:t,upstream_url:s}=e,a=(t.skills??[]).map(e=>({id:e.id??"",name:e.name??"",description:e.description??"",tags:e.tags??[],examples:e.examples??[]})),l=p.getFieldValue("agent_name")||t.name||t.provider?.organization||"",i={agent_name:l,name:t.name,description:t.description,url:s,version:t.version,protocolVersion:t.protocolVersion??"1.0",streaming:!!t.capabilities?.streaming,skills:a,iconUrl:t.iconUrl,documentationUrl:t.documentationUrl};for(let e of(eq?.credential_fields??[]).map(e=>e.key).filter(e=>/(^|_)(url|api_base|endpoint)$/i.test(e)))i[e]=s;p.setFieldsValue(i),!W&&l&&Y(`${l}-key`)},discoveryRequest:eB})})]})]}),1===h&&(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"Configure which models, agents, and MCP tools this agent is allowed to use. Leave fields empty to allow all (subject to key/team permissions)."}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Allowed Models"}),name:"entitlement_models",tooltip:"Restrict which models this agent can call. Leave empty to allow all.",children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},placeholder:en?"Loading models...":"Select models (leave empty for all)",tokenSeparators:[","],loading:en,showSearch:!0,options:el.map(e=>({label:(0,U.getModelDisplayName)(e),value:e}))})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Allowed Agents (Sub-Agents)"}),name:"entitlement_agents",tooltip:"Restrict which other agents this agent can invoke as sub-agents. Leave empty to allow all.",children:(0,t.jsx)(f.Select,{mode:"multiple",style:{width:"100%"},placeholder:ep?"Loading agents...":"Select agents (leave empty for all)",loading:ep,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:ec.map(e=>({label:e.agent_name,value:e.agent_id}))})}),(0,t.jsx)(C.Divider,{className:"my-2"}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(M.InfoCircleOutlined,{title:"Select which MCP servers or access groups this agent can access",style:{marginLeft:"4px"}})]}),name:"allowed_mcp_servers_and_groups",initialValue:{servers:[],accessGroups:[]},children:(0,t.jsx)(eM.default,{onChange:e=>p.setFieldValue("allowed_mcp_servers_and_groups",e),value:p.getFieldValue("allowed_mcp_servers_and_groups")||{servers:[],accessGroups:[]},accessToken:i??"",placeholder:"Select MCP servers or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eP.default,{accessToken:i??"",selectedServers:p.getFieldValue("allowed_mcp_servers_and_groups")?.servers??[],toolPermissions:p.getFieldValue("mcp_tool_permissions")??{},onChange:e=>p.setFieldsValue({mcp_tool_permissions:e})})})})]}),2===h&&(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Tracing"}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Require x-litellm-trace-id on calls TO this agent"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Only accept this agent being invoked with a trace-id (e.g. when used as a sub-agent)."})]}),(0,t.jsx)(_.Switch,{checked:eb,onChange:ef})]}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Require x-litellm-trace-id on calls BY this agent"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Requires LLM/MCP calls made by this agent to include x-litellm-trace-id for session tracking."})]}),(0,t.jsx)(_.Switch,{checked:ev,onChange:e=>{ek(e),e||(eS(null),eT(null))}})]})]})]}),(0,t.jsx)(C.Divider,{className:"my-0"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Budgets & Rate Limits"}),(0,t.jsxs)("div",{className:"space-y-4",children:[!ev&&(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg text-sm text-yellow-800",children:'Enable "Require x-litellm-trace-id on calls BY this agent" in Tracing to configure budgets and rate limits.'}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Session Budgets"}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Max Iterations"}),(0,t.jsx)(S.InputNumber,{className:"w-full",min:1,placeholder:"e.g. 25",disabled:!ev,value:eN,onChange:e=>eS(e)}),(0,t.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"Hard cap on LLM calls per session"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Max Budget Per Session ($)"}),(0,t.jsx)(S.InputNumber,{className:"w-full",min:.01,step:.5,placeholder:"e.g. 5.00",disabled:!ev,value:eI,onChange:e=>eT(e)}),(0,t.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"Max spend per trace before returning 429"})]})]}),(0,t.jsx)(C.Divider,{className:"my-2"}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Agent Rate Limits"}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:"Global rate limits applied across all callers of this agent."}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 100000",disabled:!ev})}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 100",disabled:!ev})})]}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700 mt-4",children:"Per-Session Rate Limits"}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:"Rate limits per session (x-litellm-trace-id). Each session gets its own counters."}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"Session TPM Limit",name:"session_tpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 10000",disabled:!ev})}),(0,t.jsx)(b.Form.Item,{label:"Session RPM Limit",name:"session_rpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 20",disabled:!ev})})]})]})]}),(0,t.jsx)(C.Divider,{className:"my-0"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Guardrails"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Apply guardrails to this agent. Selected guardrails will run on all calls made by this agent."}),(0,t.jsx)(b.Form.Item,{name:"guardrails",initialValue:[],children:(0,t.jsx)(eR.default,{accessToken:i??"",value:p.getFieldValue("guardrails")??[],onChange:e=>p.setFieldsValue({guardrails:e})})})]})]}),3===h&&(c=p.getFieldValue("agent_name")||"your-agent",(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"flex justify-center mb-6",children:(0,t.jsx)(w.Tag,{icon:(0,t.jsx)(L.RobotOutlined,{}),color:"purple",className:"px-3 py-1 text-sm",children:c})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Assign to Team"}),name:"team_id",tooltip:"Optionally assign this agent to a team. The agent and its key will belong to the selected team.",children:(0,t.jsx)(O.default,{})}),(0,t.jsx)(C.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsx)("div",{className:`p-4 border-2 rounded-lg cursor-pointer transition-colors ${"create_new"===G?"border-indigo-600 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,onClick:()=>K("create_new"),children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex items-start gap-3 flex-1",children:[(0,t.jsx)(N.Radio,{value:"create_new",checked:"create_new"===G,onChange:()=>K("create_new")}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-indigo-600"}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:"Create a new key for this agent"})]}),(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"A dedicated key scoped to this agent."}),"create_new"===G&&(0,t.jsx)("div",{className:"mt-3 space-y-3",onClick:e=>e.stopPropagation(),children:(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Key Name"}),(0,t.jsx)(v.Input,{value:W,onChange:e=>Y(e.target.value),placeholder:"e.g. my-agent-key"})]})})]})]}),(0,t.jsx)(w.Tag,{color:"green",children:"Recommended"})]})}),(0,t.jsx)("div",{className:`p-4 border-2 rounded-lg cursor-pointer transition-colors ${"existing_key"===G?"border-indigo-600 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,onClick:()=>K("existing_key"),children:(0,t.jsxs)("div",{className:"flex items-start gap-3",children:[(0,t.jsx)(N.Radio,{value:"existing_key",checked:"existing_key"===G,onChange:()=>K("existing_key")}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-gray-500"}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:"Assign an existing key"})]}),(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"Re-assign a key you already have to this agent."}),"existing_key"===G&&(0,t.jsx)("div",{className:"mt-3",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(f.Select,{showSearch:!0,style:{width:"100%"},placeholder:"Search by key name…",loading:es,value:ee,onChange:e=>et(e),filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:X.map(e=>({label:e.key_alias||e.token?.slice(0,12)+"…",value:e.token}))})})]})]})})]}),(0,t.jsx)("div",{className:"text-center mt-4",children:(0,t.jsx)("button",{type:"button",className:"text-sm text-gray-500 underline hover:text-gray-700",onClick:()=>K("skip"),children:"Skip for now — I'll assign a key later"})})]})),4===h&&(0,t.jsxs)("div",{className:"text-center py-6",children:[(0,t.jsx)(A.CheckCircleFilled,{className:"text-5xl text-green-500 mb-4",style:{fontSize:48}}),(0,t.jsx)("h3",{className:"text-xl font-semibold text-gray-900 mb-2",children:"Agent Created!"}),(0,t.jsx)("div",{className:"flex justify-center mb-4",children:(0,t.jsx)(w.Tag,{icon:(0,t.jsx)(L.RobotOutlined,{}),color:"purple",className:"px-3 py-1 text-sm",children:eh})}),eg&&(0,t.jsx)("div",{className:"mt-4 text-left max-w-md mx-auto",children:(0,t.jsx)(P.default,{apiKey:eg})}),ej&&(0,t.jsxs)("p",{className:"text-sm text-gray-600 mt-2",children:["Key ",(0,t.jsx)("span",{className:"font-medium",children:ej})," has been assigned to this agent."]}),!eg&&!ej&&"skip"===G&&(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-2",children:"No key assigned. You can create one from the Virtual Keys page."})]})]}),(0,t.jsxs)("div",{className:"flex items-center justify-between pt-6 border-t border-gray-100 mt-6",children:[(0,t.jsx)("div",{children:h>0&&h<4&&(0,t.jsx)("button",{type:"button",onClick:()=>{x(e=>Math.max(0,e-1))},className:"text-sm text-gray-600 border border-gray-300 rounded-sm px-4 py-2 hover:bg-gray-50",children:"← Back"})}),(0,t.jsxs)("div",{className:"flex gap-3",children:[h<4&&(0,t.jsx)(a.Button,{variant:"secondary",onClick:ez,children:"Cancel"}),0===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),1===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),2===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),3===h&&(0,t.jsx)(a.Button,{variant:"primary",loading:g,onClick:eH,children:g?"Creating...":"Create Agent →"}),4===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:ez,children:"Done"})]})]})]})})};var eq=e.i(708347),eV=e.i(629569),eB=e.i(197647),e$=e.i(653824),eH=e.i(881073),ez=e.i(404206),eG=e.i(723731),eK=e.i(869216),eW=e.i(530212),eY=e.i(207082),eJ=e.i(20147);let{Title:eQ,Text:eX}=eh.Typography,eZ=({keys:e,isLoading:s,onKeyClick:a})=>(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eQ,{level:4,children:"Virtual Keys"}),s?(0,t.jsx)(eX,{className:"mt-2 block",children:"Loading keys..."}):0===e.length?(0,t.jsx)(eX,{className:"mt-2 block text-gray-500",children:"No virtual key assigned to this agent."}):(0,t.jsx)("div",{className:"mt-3 flex flex-col gap-2",children:e.map(e=>(0,t.jsxs)("div",{className:"flex items-center gap-3 border border-gray-100 rounded-sm px-3 py-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-gray-400"}),(0,t.jsx)("span",{className:"font-medium",children:e.key_alias||"Unnamed key"}),e.key_name&&(0,t.jsx)("span",{className:"font-mono text-xs text-gray-500",children:e.key_name}),(0,t.jsx)(x.Tooltip,{title:e.token,children:(0,t.jsxs)(V.Button,{size:"small",type:"link",className:"font-mono text-blue-500 ml-auto",onClick:()=>a(e),children:[e.token?.slice(0,12),"..."]})})]},e.token))})]}),e0=({agent:e})=>{let s=e.litellm_params;return s?.cost_per_query===void 0&&s?.input_cost_per_token===void 0&&s?.output_cost_per_token===void 0?null:(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eV.Title,{children:"Cost Configuration"}),(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:[void 0!==s.cost_per_query&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Cost Per Query",children:["$",s.cost_per_query]}),void 0!==s.input_cost_per_token&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Input Cost Per Token",children:["$",s.input_cost_per_token]}),void 0!==s.output_cost_per_token&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Output Cost Per Token",children:["$",s.output_cost_per_token]})]})]})},e1=e=>{let t=e.litellm_params?.model||"",s=e.litellm_params?.custom_llm_provider;return"langflow"===s?"langflow":"langgraph"===s?"langgraph":"azure_ai"===s?"azure_ai_foundry":"bedrock"===s?"bedrock_agentcore":t.startsWith("langflow/")?"langflow":t.startsWith("langgraph/")?"langgraph":t.startsWith("azure_ai/agents/")?"azure_ai_foundry":t.startsWith("bedrock/agentcore/")?"bedrock_agentcore":"a2a"},e2=(e,t)=>{let s={agent_name:e.agent_name,description:e.agent_card_params?.description||""};for(let a of t.credential_fields)if(!1!==a.include_in_litellm_params)s[a.key]=e.litellm_params?.[a.key]||a.default_value||"";else if(t.model_template&&e.litellm_params?.model){let l=e.litellm_params.model,i=t.model_template.split("/"),r=l.split("/");i.forEach((e,t)=>{e===`{${a.key}}`&&r[t]&&(s[a.key]=r[t])})}return s.cost_per_query=e.litellm_params?.cost_per_query,s.input_cost_per_token=e.litellm_params?.input_cost_per_token,s.output_cost_per_token=e.litellm_params?.output_cost_per_token,s},e4=({agentId:e,onClose:i,accessToken:r,isAdmin:n})=>{let[o,c]=(0,s.useState)(null),[d,m]=(0,s.useState)(null),{data:u,isLoading:h,refetch:x}=(0,eY.useKeys)(1,100,{agentID:e}),g=u?.keys??[],[_,j]=(0,s.useState)(!0),[f,k]=(0,s.useState)(!1),[N,w]=(0,s.useState)(!1),[T]=b.Form.useForm(),[A,F]=(0,s.useState)([]),[L,D]=(0,s.useState)("a2a"),[M,P]=(0,s.useState)(null);(0,s.useEffect)(()=>{(async()=>{try{let e=await (0,y.getAgentCreateMetadata)();F(e)}catch(e){console.error("Error fetching agent metadata:",e)}})()},[]),(0,s.useEffect)(()=>{R()},[e,r]);let R=async()=>{if(r){j(!0);try{let t=await (0,y.getAgentInfo)(r,e);c(t);let s=e1(t);if(D(s),"a2a"===s)T.setFieldsValue(en(t));else{let e=A.find(e=>e.agent_type===s);e?T.setFieldsValue(e2(t,e)):T.setFieldsValue(en(t))}}catch(e){console.error("Error fetching agent info:",e),I.default.error("Failed to load agent information")}finally{j(!1)}}};(0,s.useEffect)(()=>{if(o&&A.length>0){let e=e1(o);if("a2a"!==e){let t=A.find(t=>t.agent_type===e);t&&T.setFieldsValue(e2(o,t))}}},[A,o]);let U=A.find(e=>e.agent_type===L),O=b.Form.useWatch([],T),E=(0,s.useMemo)(()=>eC(L,O||{},U),[O,U,L]),q=async t=>{if(r&&o){w(!0);try{let s;"a2a"===L?s=er(t,o):U?(s=eL(t,U)).agent_name=t.agent_name:s=er(t,o),M&&(s=ew(s,M.selected_card)),await (0,y.patchAgentCall)(r,e,s),I.default.success("Agent updated successfully"),k(!1),R()}catch(e){console.error("Error updating agent:",e),I.default.error("Failed to update agent")}finally{w(!1)}}};if(_)return(0,t.jsx)("div",{className:"p-4",children:(0,t.jsx)("div",{className:"flex justify-center items-center h-64",children:(0,t.jsx)(eu.Spin,{size:"large"})})});if(!o)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)("div",{className:"text-center",children:"Agent not found"}),(0,t.jsx)(a.Button,{onClick:i,className:"mt-4",children:"Back to Agents List"})]});let B=e=>e?new Date(e).toLocaleString():"-";return d?(0,t.jsx)(eJ.default,{keyId:d.token,keyData:d,onClose:()=>m(null),onDelete:()=>{m(null),x()},teams:null,backButtonText:"Back to Agent"}):(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Button,{icon:eW.ArrowLeftIcon,variant:"light",onClick:i,className:"mb-4",children:"Back to Agents"}),(0,t.jsx)(eV.Title,{children:o.agent_name||"Unnamed Agent"}),(0,t.jsx)(p.Text,{className:"text-gray-500 font-mono",children:o.agent_id})]}),(0,t.jsxs)(e$.TabGroup,{children:[(0,t.jsxs)(eH.TabList,{className:"mb-4",children:[(0,t.jsx)(eB.Tab,{children:"Overview"},"overview"),n?(0,t.jsx)(eB.Tab,{children:"Settings"},"settings"):(0,t.jsx)(t.Fragment,{})]}),(0,t.jsxs)(eG.TabPanels,{children:[(0,t.jsxs)(ez.TabPanel,{children:[(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,children:[(0,t.jsx)(eK.Descriptions.Item,{label:"Agent ID",children:o.agent_id}),(0,t.jsx)(eK.Descriptions.Item,{label:"Agent Name",children:o.agent_name}),(0,t.jsx)(eK.Descriptions.Item,{label:"Display Name",children:o.agent_card_params?.name||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Description",children:o.agent_card_params?.description||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"URL",children:o.agent_card_params?.url||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Version",children:o.agent_card_params?.version||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Protocol Version",children:o.agent_card_params?.protocolVersion||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Streaming",children:o.agent_card_params?.capabilities?.streaming?"Yes":"No"}),o.agent_card_params?.capabilities?.pushNotifications&&(0,t.jsx)(eK.Descriptions.Item,{label:"Push Notifications",children:"Yes"}),o.agent_card_params?.capabilities?.stateTransitionHistory&&(0,t.jsx)(eK.Descriptions.Item,{label:"State Transition History",children:"Yes"}),(0,t.jsxs)(eK.Descriptions.Item,{label:"Skills",children:[o.agent_card_params?.skills?.length||0," configured"]}),o.litellm_params?.model&&(0,t.jsx)(eK.Descriptions.Item,{label:"Model",children:o.litellm_params.model}),o.litellm_params?.make_public!==void 0&&(0,t.jsx)(eK.Descriptions.Item,{label:"Make Public",children:o.litellm_params.make_public?"Yes":"No"}),o.agent_card_params?.iconUrl&&(0,t.jsx)(eK.Descriptions.Item,{label:"Icon URL",children:o.agent_card_params.iconUrl}),o.agent_card_params?.documentationUrl&&(0,t.jsx)(eK.Descriptions.Item,{label:"Documentation URL",children:o.agent_card_params.documentationUrl}),(0,t.jsx)(eK.Descriptions.Item,{label:"TPM Limit",children:o.tpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"RPM Limit",children:o.rpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Session TPM Limit",children:o.session_tpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Session RPM Limit",children:o.session_rpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Created At",children:B(o.created_at)}),(0,t.jsx)(eK.Descriptions.Item,{label:"Updated At",children:B(o.updated_at)})]}),(0,t.jsx)(eZ,{keys:g,isLoading:h,onKeyClick:m}),o.object_permission&&(o.object_permission.mcp_servers?.length||o.object_permission.mcp_access_groups?.length||o.object_permission.mcp_tool_permissions&&Object.keys(o.object_permission.mcp_tool_permissions).length>0)&&(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eV.Title,{children:"MCP Tool Permissions"}),(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:[o.object_permission.mcp_servers&&o.object_permission.mcp_servers.length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"MCP Servers",children:o.object_permission.mcp_servers.join(", ")}),o.object_permission.mcp_access_groups&&o.object_permission.mcp_access_groups.length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"MCP Access Groups",children:o.object_permission.mcp_access_groups.join(", ")}),o.object_permission.mcp_tool_permissions&&Object.keys(o.object_permission.mcp_tool_permissions).length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"Tool permissions per server",children:(0,t.jsx)("div",{className:"space-y-1",children:Object.entries(o.object_permission.mcp_tool_permissions).map(([e,s])=>(0,t.jsxs)("div",{children:[(0,t.jsxs)("span",{className:"font-medium",children:[e,":"]})," ",Array.isArray(s)?s.join(", "):String(s)]},e))})})]})]}),(0,t.jsx)(e0,{agent:o}),o.agent_card_params?.skills&&o.agent_card_params.skills.length>0&&(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eV.Title,{children:"Skills"}),(0,t.jsx)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:o.agent_card_params.skills.map((e,s)=>(0,t.jsx)(eK.Descriptions.Item,{label:e.name||`Skill ${s+1}`,children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"ID:"})," ",e.id]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Description:"})," ",e.description]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Tags:"})," ",Array.isArray(e.tags)?e.tags.join(", "):e.tags]}),e.examples&&e.examples.length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Examples:"})," ",Array.isArray(e.examples)?e.examples.join(", "):e.examples]})]})},s))})]})]}),n&&(0,t.jsx)(ez.TabPanel,{children:(0,t.jsxs)(l.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(eV.Title,{children:"Agent Settings"}),!f&&(0,t.jsx)(a.Button,{onClick:()=>{P(null),k(!0)},children:"Edit Settings"})]}),f?(0,t.jsxs)(b.Form,{form:T,layout:"vertical",onFinish:q,children:[(0,t.jsx)(b.Form.Item,{label:"Agent ID",children:(0,t.jsx)(v.Input,{value:o.agent_id,disabled:!0})}),"a2a"===L?(0,t.jsx)(ed,{showAgentName:!0}):U?(0,t.jsx)(eD,{agentTypeInfo:U}):(0,t.jsx)(ed,{showAgentName:!0}),E&&(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eA,{accessToken:r,onApply:e=>{if(P(e),!e)return;let{selected_card:t}=e,s=(t.skills??[]).map(e=>({id:e.id??"",name:e.name??"",description:e.description??"",tags:e.tags??[],examples:e.examples??[]})),a={name:t.name,description:t.description,url:e.upstream_url,streaming:!!t.capabilities?.streaming,skills:s,iconUrl:t.iconUrl,documentationUrl:t.documentationUrl};for(let t of(U?.credential_fields??[]).map(e=>e.key).filter(e=>/(^|_)(url|api_base|endpoint)$/i.test(e)))a[t]=e.upstream_url;T.setFieldsValue(a)},discoveryRequest:E,savedAgentCard:o.agent_card_params??null})}),(0,t.jsx)(C.Divider,{}),(0,t.jsx)(eV.Title,{className:"mb-4",children:"Rate Limits"}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})})]}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"Session TPM Limit",name:"session_tpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})}),(0,t.jsx)(b.Form.Item,{label:"Session RPM Limit",name:"session_rpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})})]}),(0,t.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,t.jsx)(V.Button,{onClick:()=>{P(null),k(!1),R()},children:"Cancel"}),(0,t.jsx)(a.Button,{loading:N,children:"Save Changes"})]})]}):(0,t.jsx)(p.Text,{children:'Click "Edit Settings" to modify agent configuration.'})]})})]})]})]})};var e3=e.i(727749);e.i(622826);var e6=e.i(200208),e5=e.i(399536),e7=e.i(964471),e9=e.i(112179),e8=e.i(902555);let te=({accessToken:e,userRole:b,teams:f})=>{let[v,k]=(0,s.useState)([]),[N,w]=(0,s.useState)(!1),[C,S]=(0,s.useState)(!1),[I,T]=(0,s.useState)(!1),[A,F]=(0,s.useState)(null),[L,D]=(0,s.useState)(null),[M,P]=(0,s.useState)(!1),R=!!b&&(0,eq.isAdminRole)(b),U=async t=>{if(e){S(!0);try{let s=await (0,y.getAgentsList)(e,t??M);k(s.agents||[])}catch(e){console.error("Error fetching agents:",e)}finally{S(!1)}}};(0,s.useEffect)(()=>{U()},[e]);let O=async()=>{if(A&&e){T(!0);try{await (0,y.deleteAgentCall)(e,A.id),e3.default.success(`Agent "${A.name}" deleted successfully`),U()}catch(e){console.error("Error deleting agent:",e),e3.default.fromBackend("Failed to delete agent")}finally{T(!1),F(null)}}},E=[...v].sort((e,t)=>{let s=e.created_at?new Date(e.created_at).getTime():0;return(t.created_at?new Date(t.created_at).getTime():0)-s}),q=R?7:6;return(0,t.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,t.jsxs)("div",{className:"flex flex-col gap-2 mb-4",children:[(0,t.jsx)("h1",{className:"text-2xl font-bold",children:"Agents"}),(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"List of A2A-spec agents that are available to be used in your organization. Go to AI Hub, to make agents public."}),(0,t.jsx)(h.Alert,{message:"Why do agents need keys?",description:"Keys scope access to an agent and allow it to call MCP tools. Assign a key when creating an agent or from the Virtual Keys page.",type:"info",showIcon:!0,className:"mb-3"}),(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-4",children:[R&&(0,t.jsx)(a.Button,{onClick:()=>{L&&D(null),w(!0)},disabled:!e,children:"+ Add New Agent"}),(0,t.jsx)(x.Tooltip,{title:"When enabled, only agents with reachable URLs are shown",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.CheckCircleOutlined,{className:M?"text-green-500":"text-gray-400"}),(0,t.jsx)("span",{className:"text-sm text-gray-600",children:"Health Check"}),(0,t.jsx)(_.Switch,{size:"small",checked:M,onChange:e=>{P(e),U(e)},loading:C&&M})]})})]})]}),L?(0,t.jsx)(e4,{agentId:L,onClose:()=>D(null),accessToken:e,isAdmin:R}):(0,t.jsx)(l.Card,{children:C?(0,t.jsx)(g.Skeleton,{active:!0,paragraph:{rows:3}}):(0,t.jsxs)(i.Table,{children:[(0,t.jsx)(o.TableHead,{children:(0,t.jsxs)(d.TableRow,{children:[(0,t.jsx)(c.TableHeaderCell,{children:"Agent Name"}),(0,t.jsx)(c.TableHeaderCell,{children:"Agent ID"}),(0,t.jsx)(c.TableHeaderCell,{children:"Spend (USD)"}),(0,t.jsx)(c.TableHeaderCell,{children:"Model"}),(0,t.jsx)(c.TableHeaderCell,{children:"Created"}),(0,t.jsx)(c.TableHeaderCell,{children:"Status"}),R&&(0,t.jsx)(c.TableHeaderCell,{children:"Actions"})]})}),(0,t.jsx)(r.TableBody,{children:0===E.length?(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(n.TableCell,{colSpan:q,children:(0,t.jsx)(p.Text,{className:"text-center",children:'No agents found. Click "+ Add New Agent" to create one.'})})}):E.map(e=>(0,t.jsxs)(d.TableRow,{children:[(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(p.Text,{children:e.agent_name})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e5.IdCell,{value:e.agent_id,onClick:e=>D(e)})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e7.MoneyCell,{value:e.spend,decimals:4})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(m.Badge,{size:"xs",color:"blue",children:e.litellm_params?.model||"N/A"})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e6.DateCell,{value:e.created_at,precision:"date"})}),(0,t.jsx)(n.TableCell,{children:(e.keys?.length??0)>0?(0,t.jsx)(e9.StatusBadge,{tone:"success",label:"Active"}):(0,t.jsx)(e9.StatusBadge,{tone:"warning",label:"Needs Setup"})}),R&&(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e8.default,{variant:"Delete",onClick:()=>{F({id:e.agent_id,name:e.agent_name})}})})]},e.agent_id))})]})}),(0,t.jsx)(eE,{visible:N,onClose:()=>{w(!1)},accessToken:e,onSuccess:()=>{U()},teams:f}),A&&(0,t.jsxs)(u.Modal,{title:"Delete Agent",open:null!==A,onOk:O,onCancel:()=>{F(null)},confirmLoading:I,okText:"Delete",okButtonProps:{danger:!0},children:[(0,t.jsxs)("p",{children:["Are you sure you want to delete agent: ",A.name,"?"]}),(0,t.jsx)("p",{children:"This action cannot be undone."})]})]})};var tt=e.i(785242);e.s(["default",0,function(){let{accessToken:e,userRole:s}=(0,R.default)(),{data:a}=(0,tt.useTeams)();return(0,t.jsx)(te,{accessToken:e,userRole:s,teams:a??null})}],298805)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03rw9i0cxdgdj.js b/litellm/proxy/_experimental/out/_next/static/chunks/03rw9i0cxdgdj.js new file mode 100644 index 00000000000..bc5c4dfbdbd --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03rw9i0cxdgdj.js @@ -0,0 +1,17 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,270377,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),n=a.forwardRef(function(e,n){return a.createElement(l.default,(0,t.default)({},e,{ref:n,icon:r}))});e.s(["ExclamationCircleOutlined",0,n],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(529681),l=e.i(242064),n=e.i(517455),i=e.i(185793),o=e.i(721369),s=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let d=e=>{var{prefixCls:r,className:n,hoverable:i=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("card",r),u=(0,a.default)(`${c}-grid`,n,{[`${c}-grid-hoverable`]:i});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),g=e.i(246422),m=e.i(838378);let b=(0,g.genStyleHooks)("Card",e=>{let t=(0,m.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:a,cardHeadPadding:r,colorBorderSecondary:l,boxShadowTertiary:n,bodyPadding:i,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:n},[`${t}-head`]:(e=>{let{antCls:t,componentCls:a,headerHeight:r,headerPadding:l,tabsMarginBottom:n}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:r,marginBottom:-1,padding:`0 ${(0,c.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${a}-typography, + > ${a}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:n,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:i,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:a,cardShadow:r,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(l)} 0 0 0 ${a}, + 0 ${(0,c.unit)(l)} 0 0 ${a}, + ${(0,c.unit)(l)} ${(0,c.unit)(l)} 0 0 ${a}, + ${(0,c.unit)(l)} 0 0 0 ${a} inset, + 0 ${(0,c.unit)(l)} 0 0 ${a} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:r}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:a,actionsLiMargin:r,cardActionsIconSize:l,colorBorderSecondary:n,actionsBg:i}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:i,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:r,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${a}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${a}`]:{fontSize:l,lineHeight:(0,c.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:a}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:r}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:a,headerPadding:r,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(r)}`,background:a,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:a,headerPaddingSM:r,headerHeightSM:l,headerFontSizeSM:n}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,c.unit)(r)}`,fontSize:n,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:a}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,a;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(a=e.headerPadding)?a:e.paddingLG}});var p=e.i(792812),f=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let h=e=>{let{actionClasses:a,actions:r=[],actionStyle:l}=e;return t.createElement("ul",{className:a,style:l},r.map((e,a)=>{let l=`action-${a}`;return t.createElement("li",{style:{width:`${100/r.length}%`},key:l},t.createElement("span",null,e))}))},$=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:g,rootClassName:m,style:$,extra:y,headStyle:x={},bodyStyle:v={},title:j,loading:O,bordered:w,variant:C,size:S,type:k,cover:N,actions:E,tabList:T,children:B,activeTabKey:z,defaultActiveTabKey:M,tabBarExtraContent:R,hoverable:P,tabProps:L={},classNames:H,styles:I}=e,F=f(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:A,direction:W,card:D}=t.useContext(l.ConfigContext),[G]=(0,p.default)("card",C,w),q=e=>{var t;return(0,a.default)(null==(t=null==D?void 0:D.classNames)?void 0:t[e],null==H?void 0:H[e])},X=e=>{var t;return Object.assign(Object.assign({},null==(t=null==D?void 0:D.styles)?void 0:t[e]),null==I?void 0:I[e])},_=t.useMemo(()=>{let e=!1;return t.Children.forEach(B,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[B]),K=A("card",u),[Y,U,J]=b(K),Q=t.createElement(i.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},B),V=void 0!==z,Z=Object.assign(Object.assign({},L),{[V?"activeKey":"defaultActiveKey"]:V?z:M,tabBarExtraContent:R}),ee=(0,n.default)(S),et=ee&&"default"!==ee?ee:"large",ea=T?t.createElement(o.default,Object.assign({size:et},Z,{className:`${K}-head-tabs`,onChange:t=>{var a;null==(a=e.onTabChange)||a.call(e,t)},items:T.map(e=>{var{tab:t}=e;return Object.assign({label:t},f(e,["tab"]))})})):null;if(j||y||ea){let e=(0,a.default)(`${K}-head`,q("header")),r=(0,a.default)(`${K}-head-title`,q("title")),l=(0,a.default)(`${K}-extra`,q("extra")),n=Object.assign(Object.assign({},x),X("header"));c=t.createElement("div",{className:e,style:n},t.createElement("div",{className:`${K}-head-wrapper`},j&&t.createElement("div",{className:r,style:X("title")},j),y&&t.createElement("div",{className:l,style:X("extra")},y)),ea)}let er=(0,a.default)(`${K}-cover`,q("cover")),el=N?t.createElement("div",{className:er,style:X("cover")},N):null,en=(0,a.default)(`${K}-body`,q("body")),ei=Object.assign(Object.assign({},v),X("body")),eo=t.createElement("div",{className:en,style:ei},O?Q:B),es=(0,a.default)(`${K}-actions`,q("actions")),ed=(null==E?void 0:E.length)?t.createElement(h,{actionClasses:es,actionStyle:X("actions"),actions:E}):null,ec=(0,r.default)(F,["onTabChange"]),eu=(0,a.default)(K,null==D?void 0:D.className,{[`${K}-loading`]:O,[`${K}-bordered`]:"borderless"!==G,[`${K}-hoverable`]:P,[`${K}-contain-grid`]:_,[`${K}-contain-tabs`]:null==T?void 0:T.length,[`${K}-${ee}`]:ee,[`${K}-type-${k}`]:!!k,[`${K}-rtl`]:"rtl"===W},g,m,U,J),eg=Object.assign(Object.assign({},null==D?void 0:D.style),$);return Y(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:eg}),c,el,eo,ed))});var y=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};$.Grid=d,$.Meta=e=>{let{prefixCls:r,className:n,avatar:i,title:o,description:s}=e,d=y(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(l.ConfigContext),u=c("card",r),g=(0,a.default)(`${u}-meta`,n),m=i?t.createElement("div",{className:`${u}-meta-avatar`},i):null,b=o?t.createElement("div",{className:`${u}-meta-title`},o):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,f=b||p?t.createElement("div",{className:`${u}-meta-detail`},b,p):null;return t.createElement("div",Object.assign({},d,{className:g}),m,f)},e.s(["Card",0,$],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(908206),l=e.i(242064),n=e.i(517455),i=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a},u=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let g=e=>{let{itemPrefixCls:r,component:l,span:n,className:i,style:o,labelStyle:d,contentStyle:c,bordered:u,label:g,content:m,colon:b,type:p,styles:f}=e,{classNames:h}=t.useContext(s),$=Object.assign(Object.assign({},d),null==f?void 0:f.label),y=Object.assign(Object.assign({},c),null==f?void 0:f.content);if(u)return t.createElement(l,{colSpan:n,style:o,className:(0,a.default)(i,{[`${r}-item-${p}`]:"label"===p||"content"===p,[null==h?void 0:h.label]:(null==h?void 0:h.label)&&"label"===p,[null==h?void 0:h.content]:(null==h?void 0:h.content)&&"content"===p})},null!=g&&t.createElement("span",{style:$},g),null!=m&&t.createElement("span",{style:y},m));return t.createElement(l,{colSpan:n,style:o,className:(0,a.default)(`${r}-item`,i)},t.createElement("div",{className:`${r}-item-container`},null!=g&&t.createElement("span",{style:$,className:(0,a.default)(`${r}-item-label`,null==h?void 0:h.label,{[`${r}-item-no-colon`]:!b})},g),null!=m&&t.createElement("span",{style:y,className:(0,a.default)(`${r}-item-content`,null==h?void 0:h.content)},m)))};function m(e,{colon:a,prefixCls:r,bordered:l},{component:n,type:i,showLabel:o,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:m,prefixCls:b=r,className:p,style:f,labelStyle:h,contentStyle:$,span:y=1,key:x,styles:v},j)=>"string"==typeof n?t.createElement(g,{key:`${i}-${x||j}`,className:p,style:f,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),null==v?void 0:v.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),$),null==v?void 0:v.content)},span:y,colon:a,component:n,itemPrefixCls:b,bordered:l,label:o?e:null,content:s?m:null,type:i}):[t.createElement(g,{key:`label-${x||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),h),null==v?void 0:v.label),span:1,colon:a,component:n[0],itemPrefixCls:b,bordered:l,label:e,type:"label"}),t.createElement(g,{key:`content-${x||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),f),$),null==v?void 0:v.content),span:2*y-1,component:n[1],itemPrefixCls:b,bordered:l,content:m,type:"content"})])}let b=e=>{let a=t.useContext(s),{prefixCls:r,vertical:l,row:n,index:i,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${i}`,className:`${r}-row`},m(n,e,Object.assign({component:"th",type:"label",showLabel:!0},a))),t.createElement("tr",{key:`content-${i}`,className:`${r}-row`},m(n,e,Object.assign({component:"td",type:"content",showContent:!0},a)))):t.createElement("tr",{key:i,className:`${r}-row`},m(n,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},a)))};e.i(296059);var p=e.i(915654),f=e.i(183293),h=e.i(246422),$=e.i(838378);let y=(0,h.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:a,itemPaddingBottom:r,itemPaddingEnd:l,colonMarginRight:n,colonMarginLeft:i,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,f.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:a}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:a,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},f.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:a,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:r,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(i)} ${(0,p.unit)(n)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,$.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var x=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let v=e=>{let g,{prefixCls:m,title:p,extra:f,column:h,colon:$=!0,bordered:v,layout:j,children:O,className:w,rootClassName:C,style:S,size:k,labelStyle:N,contentStyle:E,styles:T,items:B,classNames:z}=e,M=x(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:R,direction:P,className:L,style:H,classNames:I,styles:F}=(0,l.useComponentConfig)("descriptions"),A=R("descriptions",m),W=(0,i.default)(),D=t.useMemo(()=>{var e;return"number"==typeof h?h:null!=(e=(0,r.matchScreen)(W,Object.assign(Object.assign({},o),h)))?e:3},[W,h]),G=(g=t.useMemo(()=>B||(0,d.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[B,O]),t.useMemo(()=>g.map(e=>{var{span:t}=e,a=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},a),{filled:!0}):Object.assign(Object.assign({},a),{span:"number"==typeof t?t:(0,r.matchScreen)(W,t)})}),[g,W])),q=(0,n.default)(k),X=((e,a)=>{let[r,l]=(0,t.useMemo)(()=>{let t,r,l,n;return t=[],r=[],l=!1,n=0,a.filter(e=>e).forEach(a=>{let{filled:i}=a,o=u(a,["filled"]);if(i){r.push(o),t.push(r),r=[],n=0;return}let s=e-n;(n+=a.span||1)>=e?(n>e?(l=!0,r.push(Object.assign(Object.assign({},o),{span:s}))):r.push(o),t.push(r),r=[],n=0):r.push(o)}),r.length>0&&t.push(r),[t=t.map(t=>{let a=t.reduce((e,t)=>e+(t.span||1),0);if(a({labelStyle:N,contentStyle:E,styles:{content:Object.assign(Object.assign({},F.content),null==T?void 0:T.content),label:Object.assign(Object.assign({},F.label),null==T?void 0:T.label)},classNames:{label:(0,a.default)(I.label,null==z?void 0:z.label),content:(0,a.default)(I.content,null==z?void 0:z.content)}}),[N,E,T,z,I,F]);return _(t.createElement(s.Provider,{value:U},t.createElement("div",Object.assign({className:(0,a.default)(A,L,I.root,null==z?void 0:z.root,{[`${A}-${q}`]:q&&"default"!==q,[`${A}-bordered`]:!!v,[`${A}-rtl`]:"rtl"===P},w,C,K,Y),style:Object.assign(Object.assign(Object.assign(Object.assign({},H),F.root),null==T?void 0:T.root),S)},M),(p||f)&&t.createElement("div",{className:(0,a.default)(`${A}-header`,I.header,null==z?void 0:z.header),style:Object.assign(Object.assign({},F.header),null==T?void 0:T.header)},p&&t.createElement("div",{className:(0,a.default)(`${A}-title`,I.title,null==z?void 0:z.title),style:Object.assign(Object.assign({},F.title),null==T?void 0:T.title)},p),f&&t.createElement("div",{className:(0,a.default)(`${A}-extra`,I.extra,null==z?void 0:z.extra),style:Object.assign(Object.assign({},F.extra),null==T?void 0:T.extra)},f)),t.createElement("div",{className:`${A}-view`},t.createElement("table",null,t.createElement("tbody",null,X.map((e,a)=>t.createElement(b,{key:a,index:a,colon:$,prefixCls:A,vertical:"vertical"===j,bordered:v,row:e}))))))))};v.Item=({children:e})=>e,e.s(["Descriptions",0,v],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),a=e.i(732961),r=e.i(289882),l=e.i(170517),n=e.i(628882),i=e.i(320890),o=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),m=e.i(135551);let b=(e,t)=>new m.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new m.FastColor(e).lighten(t).toHexString(),f=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},h=(e,t)=>{let a=e||"#000",r=t||"#fff";return{colorBgBase:a,colorTextBase:r,colorText:b(r,.85),colorTextSecondary:b(r,.65),colorTextTertiary:b(r,.45),colorTextQuaternary:b(r,.25),colorFill:b(r,.18),colorFillSecondary:b(r,.12),colorFillTertiary:b(r,.08),colorFillQuaternary:b(r,.04),colorBgSolid:b(r,.95),colorBgSolidHover:b(r,1),colorBgSolidActive:b(r,.9),colorBgElevated:p(a,12),colorBgContainer:p(a,8),colorBgLayout:p(a,0),colorBgSpotlight:p(a,26),colorBgBlur:b(r,.04),colorBorder:p(a,26),colorBorderSecondary:p(a,19)}},$={defaultSeed:i.defaultConfig.token,useToken:function(){let[e,t,a]=(0,o.useToken)();return{theme:e,token:t,hashId:a}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let a=Object.keys(l.defaultPresetColors).map(t=>{let a=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,r,l)=>(e[`${t}-${l+1}`]=a[l],e[`${t}${l+1}`]=a[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),r=null!=t?t:(0,s.default)(e),n=(0,g.default)(e,{generateColorPalettes:f,generateNeutralColorPalettes:h});return Object.assign(Object.assign(Object.assign(Object.assign({},r),a),n),{colorPrimaryBg:n.colorPrimaryBorder,colorPrimaryBgHover:n.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let a=null!=t?t:(0,s.default)(e),r=a.fontSizeSM,l=a.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},a),function(e){let{sizeUnit:t,sizeStep:a}=e,r=a-2;return{sizeXXL:t*(r+10),sizeXL:t*(r+6),sizeLG:t*(r+2),sizeMD:t*(r+2),sizeMS:t*(r+1),size:t*r,sizeSM:t*r,sizeXS:t*(r-1),sizeXXS:t*(r-1)}}(null!=t?t:e)),(0,c.default)(r)),{controlHeight:l}),(0,d.default)(Object.assign(Object.assign({},a),{controlHeight:l})))},getDesignToken:e=>{let i=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):r.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,a.getComputedToken)(o,{override:null==e?void 0:e.token},i,n.default)},defaultConfig:i.defaultConfig,_internalContext:i.DesignTokenContext};e.s(["theme",0,$],368869)},127952,e=>{"use strict";var t=e.i(843476),a=e.i(560445),r=e.i(175712),l=e.i(869216),n=e.i(311451),i=e.i(212931),o=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:m,resourceInformationTitle:b,resourceInformation:p,onCancel:f,onOk:h,confirmLoading:$,requiredConfirmation:y}){let{Title:x,Text:v}=o.Typography,{token:j}=s.theme.useToken(),[O,w]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&w("")},[e]),(0,t.jsx)(i.Modal,{title:u,open:e,onOk:h,onCancel:f,confirmLoading:$,okText:$?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!y&&O!==y||$},cancelButtonProps:{disabled:$},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(a.Alert,{message:g,type:"warning"}),(0,t.jsx)(r.Card,{title:b,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder}},style:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:a,...r})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(v,{...r,children:a??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(v,{children:m})}),y&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(v,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(v,{children:"Type "}),(0,t.jsx)(v,{strong:!0,type:"danger",children:y}),(0,t.jsx)(v,{children:" to confirm deletion:"})]}),(0,t.jsx)(n.Input,{value:O,onChange:e=>w(e.target.value),placeholder:y,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:j.colorError}}),autoFocus:!0})]})]})})}])},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(242064),l=e.i(529681);let n=e=>{let{prefixCls:r,className:l,style:n,size:i,shape:o}=e,s=(0,a.default)({[`${r}-lg`]:"large"===i,[`${r}-sm`]:"small"===i}),d=(0,a.default)({[`${r}-circle`]:"circle"===o,[`${r}-square`]:"square"===o,[`${r}-round`]:"round"===o}),c=t.useMemo(()=>"number"==typeof i?{width:i,height:i,lineHeight:`${i}px`}:{},[i]);return t.createElement("span",{className:(0,a.default)(r,s,d,l),style:Object.assign(Object.assign({},c),n)})};e.i(296059);var i=e.i(694758),o=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new i.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,o.unit)(e)}),g=e=>Object.assign({width:e},u(e)),m=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),b=e=>Object.assign({width:e},u(e)),p=(e,t,a)=>{let{skeletonButtonCls:r}=e;return{[`${a}${r}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${a}${r}-round`]:{borderRadius:t}}},f=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),h=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:a}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:a,skeletonTitleCls:r,skeletonParagraphCls:l,skeletonButtonCls:n,skeletonInputCls:i,skeletonImageCls:o,controlHeight:s,controlHeightLG:d,controlHeightSM:u,gradientFromColor:h,padding:$,marginSM:y,borderRadius:x,titleHeight:v,blockRadius:j,paragraphLiHeight:O,controlHeightXS:w,paragraphMarginTop:C}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:$,verticalAlign:"top",[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:h},g(s)),[`${a}-circle`]:{borderRadius:"50%"},[`${a}-lg`]:Object.assign({},g(d)),[`${a}-sm`]:Object.assign({},g(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[r]:{width:"100%",height:v,background:h,borderRadius:j,[`+ ${l}`]:{marginBlockStart:u}},[l]:{padding:0,"> li":{width:"100%",height:O,listStyle:"none",background:h,borderRadius:j,"+ li":{marginBlockStart:w}}},[`${l}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${r}, ${l} > li`]:{borderRadius:x}}},[`${t}-with-avatar ${t}-content`]:{[r]:{marginBlockStart:y,[`+ ${l}`]:{marginBlockStart:C}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:a,controlHeight:r,controlHeightLG:l,controlHeightSM:n,gradientFromColor:i,calc:o}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:t,width:o(r).mul(2).equal(),minWidth:o(r).mul(2).equal()},f(r,o))},p(e,r,a)),{[`${a}-lg`]:Object.assign({},f(l,o))}),p(e,l,`${a}-lg`)),{[`${a}-sm`]:Object.assign({},f(n,o))}),p(e,n,`${a}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:a,controlHeight:r,controlHeightLG:l,controlHeightSM:n}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:a},g(r)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},g(l)),[`${t}${t}-sm`]:Object.assign({},g(n))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:a,skeletonInputCls:r,controlHeightLG:l,controlHeightSM:n,gradientFromColor:i,calc:o}=e;return{[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:a},m(t,o)),[`${r}-lg`]:Object.assign({},m(l,o)),[`${r}-sm`]:Object.assign({},m(n,o))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:a,gradientFromColor:r,borderRadiusSM:l,calc:n}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:r,borderRadius:l},b(n(a).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(a)),{maxWidth:n(a).mul(4).equal(),maxHeight:n(a).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[n]:{width:"100%"},[i]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${r}, + ${l} > li, + ${a}, + ${n}, + ${i}, + ${o} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:a(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:a}=e;return{color:t,colorGradientEnd:a,gradientFromColor:t,gradientToColor:a,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),$=e=>{let{prefixCls:r,className:l,style:n,rows:i=0}=e,o=Array.from({length:i}).map((a,r)=>t.createElement("li",{key:r,style:{width:((e,t)=>{let{width:a,rows:r=2}=t;return Array.isArray(a)?a[e]:r-1===e?a:void 0})(r,e)}}));return t.createElement("ul",{className:(0,a.default)(r,l),style:n},o)},y=({prefixCls:e,className:r,width:l,style:n})=>t.createElement("h3",{className:(0,a.default)(e,r),style:Object.assign({width:l},n)});function x(e){return e&&"object"==typeof e?e:{}}let v=e=>{let{prefixCls:l,loading:i,className:o,rootClassName:s,style:d,children:c,avatar:u=!1,title:g=!0,paragraph:m=!0,active:b,round:p}=e,{getPrefixCls:f,direction:v,className:j,style:O}=(0,r.useComponentConfig)("skeleton"),w=f("skeleton",l),[C,S,k]=h(w);if(i||!("loading"in e)){let e,r,l=!!u,i=!!g,c=!!m;if(l){let a=Object.assign(Object.assign({prefixCls:`${w}-avatar`},i&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),x(u));e=t.createElement("div",{className:`${w}-header`},t.createElement(n,Object.assign({},a)))}if(i||c){let e,a;if(i){let a=Object.assign(Object.assign({prefixCls:`${w}-title`},!l&&c?{width:"38%"}:l&&c?{width:"50%"}:{}),x(g));e=t.createElement(y,Object.assign({},a))}if(c){let e,r=Object.assign(Object.assign({prefixCls:`${w}-paragraph`},(e={},l&&i||(e.width="61%"),!l&&i?e.rows=3:e.rows=2,e)),x(m));a=t.createElement($,Object.assign({},r))}r=t.createElement("div",{className:`${w}-content`},e,a)}let f=(0,a.default)(w,{[`${w}-with-avatar`]:l,[`${w}-active`]:b,[`${w}-rtl`]:"rtl"===v,[`${w}-round`]:p},j,o,s,S,k);return C(t.createElement("div",{className:f,style:Object.assign(Object.assign({},O),d)},e,r))}return null!=c?c:null};v.Button=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,block:c=!1,size:u="default"}=e,{getPrefixCls:g}=t.useContext(r.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,l.default)(e,["prefixCls"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-button`,size:u},$))))},v.Avatar=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,shape:c="circle",size:u="default"}=e,{getPrefixCls:g}=t.useContext(r.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,l.default)(e,["prefixCls","className"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-avatar`,shape:c,size:u},$))))},v.Input=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,block:c,size:u="default"}=e,{getPrefixCls:g}=t.useContext(r.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,l.default)(e,["prefixCls"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-input`,size:u},$))))},v.Image=e=>{let{prefixCls:l,className:n,rootClassName:i,style:o,active:s}=e,{getPrefixCls:d}=t.useContext(r.ConfigContext),c=d("skeleton",l),[u,g,m]=h(c),b=(0,a.default)(c,`${c}-element`,{[`${c}-active`]:s},n,i,g,m);return u(t.createElement("div",{className:b},t.createElement("div",{className:(0,a.default)(`${c}-image`,n),style:o},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},v.Node=e=>{let{prefixCls:l,className:n,rootClassName:i,style:o,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(r.ConfigContext),u=c("skeleton",l),[g,m,b]=h(u),p=(0,a.default)(u,`${u}-element`,{[`${u}-active`]:s},m,n,i,b);return g(t.createElement("div",{className:p},t.createElement("div",{className:(0,a.default)(`${u}-image`,n),style:o},d)))},e.s(["default",0,v],185793)},922611,e=>{"use strict";var t=e.i(271645),a=e.i(175066);function r(){}let l=t.createContext({add:r,remove:r});e.s(["usePanelRef",0,function(e){let r=t.useContext(l),n=t.useRef(null);return(0,a.default)(t=>{if(t){let a=e?t.querySelector(e):t;a&&(r.add(a),n.current=a)}else r.remove(n.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let a=(e,t=0,a=!1,r=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!r)return"-";let l={minimumFractionDigits:t,maximumFractionDigits:t};if(!a)return e.toLocaleString("en-US",l);let n=e<0?"-":"",i=Math.abs(e),o=i,s="";return i>=1e6?(o=i/1e6,s="M"):i>=1e3&&(o=i/1e3,s="K"),`${n}${o.toLocaleString("en-US",l)}${s}`},r=async(e,a="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,a);try{return await navigator.clipboard.writeText(e),t.default.success(a),!0}catch(t){return console.error("Clipboard API failed: ",t),l(e,a)}},l=(e,a)=>{try{let r=document.createElement("textarea");r.value=e,r.style.position="fixed",r.style.left="-999999px",r.style.top="-999999px",r.setAttribute("readonly",""),document.body.appendChild(r),r.focus(),r.select();let l=document.execCommand("copy");if(document.body.removeChild(r),l)return t.default.success(a),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,r,"formatNumberWithCommas",0,a,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let r=a(e,t,!1,!1);if(0===Number(r.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${r}`},"updateExistingKeys",0,function(e,t){let a=structuredClone(e);for(let[e,r]of Object.entries(t))e in a&&(a[e]=r);return a}])},112179,581070,e=>{"use strict";var t=e.i(843476),a=e.i(487486),r=e.i(115504),l=e.i(746798);function n({content:e,trigger:a}){return(0,t.jsx)(l.TooltipProvider,{delay:300,children:(0,t.jsxs)(l.Tooltip,{children:[(0,t.jsx)(l.TooltipTrigger,{render:a}),(0,t.jsx)(l.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,n],581070);let i={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:l,tooltip:o,dataTestId:s}){let d=(0,t.jsx)(a.Badge,{variant:"outline","data-testid":s,className:(0,r.cn)("whitespace-nowrap font-normal",i[e]),children:l});return o?(0,t.jsx)(n,{content:o,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),a=e.i(843476);let r=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],l=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:n="datetime",fallback:i="-"}){let o,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,a.jsx)("span",{className:"text-muted-foreground",children:i}):(0,a.jsx)(t.CellTooltip,{content:(o=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${r[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${l(c.getHours())}:${l(c.getMinutes())}:${l(c.getSeconds())}`,`${s}, ${d} (${o})`),trigger:(0,a.jsx)("span",{className:"whitespace-nowrap",children:"date"===n?`${r[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${r[c.getMonth()]} ${c.getDate()}, ${l(c.getHours())}:${l(c.getMinutes())}:${l(c.getSeconds())}`})})}],200208);var n=e.i(174886),i=e.i(115504),o=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:r="pill",onClick:l,copyable:d=!1,truncate:c=!0,fallback:u="-",tooltip:g,disabled:m=!1,dataTestId:b,className:p}){if(!e)return(0,a.jsx)("span",{className:"text-muted-foreground",children:u});let f=!!l&&!m,h=(0,i.cn)(s[r].base,f&&s[r].clickable,c&&"block max-w-[15ch] truncate",m&&"opacity-50",p),$=f?(0,a.jsx)("button",{type:"button",className:h,"data-testid":b,onClick:()=>l(e),children:e}):(0,a.jsx)("span",{className:h,"data-testid":b,children:e}),y=(0,a.jsx)(t.CellTooltip,{content:g??e,trigger:$});return d?(0,a.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[y,(0,a.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,o.copyToClipboard)(e)},children:(0,a.jsx)(n.Copy,{className:"size-3"})})]}):y}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:r="-",showZero:l=!1}){return null==e||Number.isNaN(e)?(0,a.jsx)("span",{className:"text-muted-foreground",children:r}):0===e?l?(0,a.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,o.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,a.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,a.jsx)("span",{className:"whitespace-nowrap",children:(0,o.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,a],68155)},360820,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,a],360820)},871943,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,a],871943)},269200,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("Table"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement("div",{className:(0,r.tremorTwMerge)(l("root"),"overflow-auto",o)},a.default.createElement("table",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),i))});n.displayName="Table",e.s(["Table",0,n],269200)},427612,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableHead"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("thead",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",o)},s),i))});n.displayName="TableHead",e.s(["TableHead",0,n],427612)},64848,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableHeaderCell"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("th",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",o)},s),i))});n.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,n],64848)},942232,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableBody"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("tbody",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",o)},s),i))});n.displayName="TableBody",e.s(["TableBody",0,n],942232)},496020,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableRow"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("tr",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("row"),o)},s),i))});n.displayName="TableRow",e.s(["TableRow",0,n],496020)},977572,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableCell"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("td",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"align-middle whitespace-nowrap text-left p-4",o)},s),i))});n.displayName="TableCell",e.s(["TableCell",0,n],977572)},389083,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(829087),l=e.i(480731),n=e.i(95779),i=e.i(444755),o=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,o.makeClassName)("Badge"),u=a.default.forwardRef((e,u)=>{let{color:g,icon:m,size:b=l.Sizes.SM,tooltip:p,className:f,children:h}=e,$=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),y=m||null,{tooltipProps:x,getReferenceProps:v}=(0,r.useTooltip)();return a.default.createElement("span",Object.assign({ref:(0,o.mergeRefs)([u,x.refs.setReference]),className:(0,i.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",g?(0,i.tremorTwMerge)((0,o.getColorClassNames)(g,n.colorPalette.background).bgColor,(0,o.getColorClassNames)(g,n.colorPalette.iconText).textColor,(0,o.getColorClassNames)(g,n.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,i.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[b].paddingX,s[b].paddingY,s[b].fontSize,f)},v,$),a.default.createElement(r.default,Object.assign({text:p},x)),y?a.default.createElement(y,{className:(0,i.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[b].height,d[b].width)}):null,a.default.createElement("span",{className:(0,i.tremorTwMerge)(c("text"),"whitespace-nowrap")},h))});u.displayName="Badge",e.s(["Badge",0,u],389083)},530212,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,a],530212)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03sib2ibxxpji.js b/litellm/proxy/_experimental/out/_next/static/chunks/03sib2ibxxpji.js new file mode 100644 index 00000000000..ee0c24f0032 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03sib2ibxxpji.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),s=e.i(529681);let l=e=>{let{prefixCls:a,className:s,style:l,size:n,shape:i}=e,o=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),c=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),d=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,o,c,s),style:Object.assign(Object.assign({},d),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),o=e.i(246422),c=e.i(838378);let d=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,i.unit)(e)}),u=e=>Object.assign({width:e},m(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),p=e=>Object.assign({width:e},m(e)),x=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),f=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:s,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:o,controlHeightLG:c,controlHeightSM:m,gradientFromColor:f,padding:b,marginSM:v,borderRadius:j,titleHeight:w,blockRadius:N,paragraphLiHeight:y,controlHeightXS:k,paragraphMarginTop:$}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:b,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},u(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},u(c)),[`${r}-sm`]:Object.assign({},u(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:f,borderRadius:N,[`+ ${s}`]:{marginBlockStart:m}},[s]:{padding:0,"> li":{width:"100%",height:y,listStyle:"none",background:f,borderRadius:N,"+ li":{marginBlockStart:k}}},[`${s}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${s} > li`]:{borderRadius:j}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:v,[`+ ${s}`]:{marginBlockStart:$}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},x(e,a,r)),{[`${r}-lg`]:Object.assign({},h(s,i))}),x(e,s,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),x(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},u(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},u(s)),[`${t}${t}-sm`]:Object.assign({},u(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(s,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:s,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:s},p(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},p(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${s} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),b=e=>{let{prefixCls:a,className:s,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,s),style:l},i)},v=({prefixCls:e,className:a,width:s,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:s},l)});function j(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:s,loading:n,className:i,rootClassName:o,style:c,children:d,avatar:m=!1,title:u=!0,paragraph:g=!0,active:p,round:x}=e,{getPrefixCls:h,direction:w,className:N,style:y}=(0,a.useComponentConfig)("skeleton"),k=h("skeleton",s),[$,C,T]=f(k);if(n||!("loading"in e)){let e,a,s=!!m,n=!!u,d=!!g;if(s){let r=Object.assign(Object.assign({prefixCls:`${k}-avatar`},n&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),j(m));e=t.createElement("div",{className:`${k}-header`},t.createElement(l,Object.assign({},r)))}if(n||d){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${k}-title`},!s&&d?{width:"38%"}:s&&d?{width:"50%"}:{}),j(u));e=t.createElement(v,Object.assign({},r))}if(d){let e,a=Object.assign(Object.assign({prefixCls:`${k}-paragraph`},(e={},s&&n||(e.width="61%"),!s&&n?e.rows=3:e.rows=2,e)),j(g));r=t.createElement(b,Object.assign({},a))}a=t.createElement("div",{className:`${k}-content`},e,r)}let h=(0,r.default)(k,{[`${k}-with-avatar`]:s,[`${k}-active`]:p,[`${k}-rtl`]:"rtl"===w,[`${k}-round`]:x},N,i,o,C,T);return $(t.createElement("div",{className:h,style:Object.assign(Object.assign({},y),c)},e,a))}return null!=d?d:null};w.Button=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d=!1,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:m},b))))},w.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,shape:d="circle",size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls","className"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:d,size:m},b))))},w.Input=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:m},b))))},w.Image=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),d=c("skeleton",s),[m,u,g]=f(d),p=(0,r.default)(d,`${d}-element`,{[`${d}-active`]:o},l,n,u,g);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${d}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},w.Node=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o,children:c}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),m=d("skeleton",s),[u,g,p]=f(m),x=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:o},g,l,n,p);return u(t.createElement("div",{className:x},t.createElement("div",{className:(0,r.default)(`${m}-image`,l),style:i},c)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let s=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(s),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let l=e<0?"-":"",n=Math.abs(e),i=n,o="";return n>=1e6?(i=n/1e6,o="M"):n>=1e3&&(i=n/1e3,o="K"),`${l}${i.toLocaleString("en-US",s)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),s=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(s.TooltipProvider,{delay:300,children:(0,t.jsxs)(s.Tooltip,{children:[(0,t.jsx)(s.TooltipTrigger,{render:r}),(0,t.jsx)(s.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:s,tooltip:i,dataTestId:o}){let c=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:s});return i?(0,t.jsx)(l,{content:i,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,o,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`,`${o}, ${c} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${a[d.getMonth()]} ${d.getDate()}, ${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:s,copyable:c=!1,truncate:d=!0,fallback:m="-",tooltip:u,disabled:g=!1,dataTestId:p,className:x}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let h=!!s&&!g,f=(0,n.cn)(o[a].base,h&&o[a].clickable,d&&"block max-w-[15ch] truncate",g&&"opacity-50",x),b=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":p,onClick:()=>s(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":p,children:e}),v=(0,r.jsx)(t.CellTooltip,{content:u??e,trigger:b});return c?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[v,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):v}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:s=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?s?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},o),n))});l.displayName="Table",e.s(["Table",0,l],269200)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},o),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("row"),i)},o),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},o),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},o),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",i)},o),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),s=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let o={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,i.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:u,icon:g,size:p=s.Sizes.SM,tooltip:x,className:h,children:f}=e,b=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),v=g||null,{tooltipProps:j,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,n.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,n.tremorTwMerge)((0,i.getColorClassNames)(u,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(u,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(u,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),o[p].paddingX,o[p].paddingY,o[p].fontSize,h)},w,b),r.default.createElement(a.default,Object.assign({text:x},j)),v?r.default.createElement(v,{className:(0,n.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(d("text"),"whitespace-nowrap")},f))});m.displayName="Badge",e.s(["Badge",0,m],389083)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},384767,e=>{"use strict";var t=e.i(843476),r=e.i(599724),a=e.i(271645),s=e.i(389083);let l=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var n=e.i(602869);let i=function({vectorStores:e,accessToken:i}){let[o,c]=(0,a.useState)([]);return(0,a.useEffect)(()=>{(async()=>{if(i&&0!==e.length)try{let e=await (0,n.vectorStoreListCall)(i);e.data&&c(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[i,e.length]),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,t.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,r)=>{let a;return(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(a=o.find(t=>t.vector_store_id===e))?`${a.vector_store_name||a.vector_store_id} (${a.vector_store_id})`:e},r)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},o=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var c=e.i(871943),d=e.i(502547),m=e.i(592968),u=e.i(234713);let g=function({mcpServers:e,mcpAccessGroups:l=[],mcpToolPermissions:i={},mcpToolsets:g=[],accessToken:p}){let[x,h]=(0,a.useState)([]),[f,b]=(0,a.useState)([]),[v,j]=(0,a.useState)(new Set),[w,N]=(0,a.useState)(new Set);(0,a.useEffect)(()=>{(async()=>{if(p&&e.length>0)try{let e=await (0,n.fetchMCPServers)(p);e&&Array.isArray(e)?h(e):e.data&&Array.isArray(e.data)&&h(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[p,e.length]),(0,a.useEffect)(()=>{(async()=>{if(p&&g.length>0)try{let e=await (0,n.fetchMCPToolsets)(p),t=Array.isArray(e)?e.filter(e=>g.includes(e.toolset_id)):[];b(t)}catch(e){console.error("Error fetching toolsets:",e)}})()},[p,g.length]);let y=e.includes(u.NO_MCP_SERVERS_SENTINEL),k=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),$=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],C=$.length+g.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,t.jsx)(s.Badge,{color:y?"red":"blue",size:"xs",children:y?"Blocked":k?"All":C})]}),y?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-red-400"}),(0,t.jsx)(r.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):k?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-400"}),(0,t.jsx)(r.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):C>0?(0,t.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[$.map((e,r)=>{let a="server"===e.type?i[e.value]:void 0,s=a&&a.length>0,l=v.has(e.value);return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>{var t;return s&&(t=e.value,void j(e=>{let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=x.find(t=>t.server_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.alias} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a.length}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===a.length?"tool":"tools"}),l?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&l&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.map((e,r)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},r))})})]},r)}),g.length>0&&g.map((e,r)=>{let a=f.find(t=>t.toolset_id===e),s=w.has(e),l=a?.tools.length??0;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>l>0&&void N(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${l>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:a?.toolset_name??e}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),l>0&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:l}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===l?"tool":"tools"}),s?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),l>0&&s&&a&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.tools.map((e,r)=>(0,t.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,t.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},r))})})]},`toolset-${r}`)})]}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},p=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),x=function({agents:e,agentAccessGroups:l=[],accessToken:i}){let[o,c]=(0,a.useState)([]);(0,a.useEffect)(()=>{(async()=>{if(i&&e.length>0)try{let e=await (0,n.getAgentsList)(i);e&&e.agents&&Array.isArray(e.agents)&&c(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[i,e.length]);let d=[...e.map(e=>({type:"agent",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],u=d.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-purple-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,t.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,t.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:d.map((e,r)=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=o.find(t=>t.agent_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.agent_name} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},r))}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:a="card",className:s="",accessToken:l}){let n=e?.vector_stores||[],o=e?.mcp_servers||[],c=e?.mcp_access_groups||[],d=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],p=e?.agent_access_groups||[],h=e?.search_tools||[],f=(0,t.jsxs)("div",{className:"card"===a?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,t.jsx)(i,{vectorStores:n,accessToken:l}),(0,t.jsx)(g,{mcpServers:o,mcpAccessGroups:c,mcpToolPermissions:d,mcpToolsets:m,accessToken:l}),(0,t.jsx)(x,{agents:u,agentAccessGroups:p,accessToken:l}),(0,t.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,t.jsx)(r.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===h.length?(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-700",children:h.join(", ")})]})]});return"card"===a?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),f]}):(0,t.jsxs)("div",{className:`${s}`,children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),f]})}],384767)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03zxkn.2-qj65.js b/litellm/proxy/_experimental/out/_next/static/chunks/03zxkn.2-qj65.js new file mode 100644 index 00000000000..b275472205d --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03zxkn.2-qj65.js @@ -0,0 +1,31 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,389543,e=>{"use strict";var t=e.i(843476),r=e.i(271645),l=e.i(304967),a=e.i(269200),s=e.i(427612),n=e.i(496020),i=e.i(64848),o=e.i(977572),d=e.i(942232),c=e.i(599724),u=e.i(994388),g=e.i(752978),m=e.i(793130),p=e.i(404206),f=e.i(723731),h=e.i(653824),y=e.i(881073),x=e.i(197647),b=e.i(602869),_=e.i(28651),j=e.i(68155);e.i(622826);var w=e.i(112179),C=e.i(464571),S=e.i(727749),k=e.i(158392);let v=({accessToken:e,userRole:l,userID:a})=>{let[s,n]=(0,r.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)({}),[u,g]=(0,r.useState)({});(0,r.useEffect)(()=>{e&&l&&a&&((0,b.getCallbacksCall)(e,a,l).then(e=>{let t=e.router_settings;"model_group_retry_policy"in t&&delete t.model_group_retry_policy;let r=t.routing_strategy||null;n(e=>({...e,routerSettings:t,selectedStrategy:r}))}),(0,b.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),c(t);let r=e.fields.find(e=>"routing_strategy"===e.field_name);r?.options&&o(r.options),e.routing_strategy_descriptions&&g(e.routing_strategy_descriptions);let l=e.fields.find(e=>"enable_tag_filtering"===e.field_name);l?.field_value!==null&&l?.field_value!==void 0&&n(e=>({...e,enableTagFiltering:l.field_value}))}}))},[e,l,a]);let m=async()=>{if(!e)return;let t=s.routerSettings,r=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),l=new Set(["model_group_alias"]),a=new Set(["retry_policy","model_group_retry_policy","routing_groups"]),n=Object.fromEntries(Object.entries({...t,enable_tag_filtering:s.enableTagFiltering}).map(([e,t])=>{if(a.has(e))return null;if("routing_strategy_args"!==e&&"routing_strategy"!==e&&"enable_tag_filtering"!==e){let a=document.querySelector(`input[name="${e}"]`),s=((e,t,a)=>{if(void 0===t)return a;let s=t.trim();if("null"===s.toLowerCase())return null;if(r.has(e)){let e=Number(s);return Number.isNaN(e)?a:e}if(l.has(e)){if(""===s)return null;try{return JSON.parse(s)}catch{return a}}return"true"===s.toLowerCase()||"false"!==s.toLowerCase()&&s})(e,a?.value,t);return[e,s]}if("routing_strategy"===e)return[e,s.selectedStrategy];if("enable_tag_filtering"===e)return[e,s.enableTagFiltering];if("routing_strategy_args"===e&&"latency-based-routing"===s.selectedStrategy){let e={},t=document.querySelector('input[name="lowest_latency_buffer"]'),r=document.querySelector('input[name="ttl"]');return t?.value&&(e.lowest_latency_buffer=Number(t.value)),r?.value&&(e.ttl=Number(r.value)),["routing_strategy_args",e]}return null}).filter(e=>null!=e));try{await (0,b.setCallbacksCall)(e,{router_settings:n}),S.default.success("router settings updated successfully")}catch(e){S.default.fromBackend("Failed to update router settings: "+e)}};return e?(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsx)(k.default,{value:s,onChange:n,routerFieldsMetadata:d,availableRoutingStrategies:i,routingStrategyDescriptions:u}),(0,t.jsxs)("div",{className:"border-t border-gray-200 pt-6 flex justify-end gap-3",children:[(0,t.jsx)(C.Button,{onClick:()=>window.location.reload(),children:"Reset"}),(0,t.jsx)(C.Button,{type:"primary",onClick:m,children:"Save Changes"})]})]}):null};e.i(247167);var T=e.i(368670);let N=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14 5l7 7m0 0l-7 7m7-7H3"}))});var A=e.i(122577),I=e.i(592968),F=e.i(898586),L=e.i(356449),M=e.i(127952),O=e.i(418371),B=e.i(708347),E=e.i(888259),R=e.i(695411),P=e.i(212931);let D=(0,e.i(475254).default)("arrow-right",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]]);function $({open:e,onCancel:r,children:l}){return(0,t.jsx)(P.Modal,{title:(0,t.jsx)("div",{className:"pb-4 border-b border-gray-100",children:(0,t.jsxs)("div",{className:"flex items-center gap-2 text-gray-800",children:[(0,t.jsx)("div",{className:"p-2 bg-indigo-50 rounded-lg",children:(0,t.jsx)(D,{className:"w-5 h-5 text-indigo-600"})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h2",{className:"text-lg font-bold m-0",children:"Configure Model Fallbacks"}),(0,t.jsx)("p",{className:"text-sm text-gray-500 font-normal m-0",children:"Manage multiple fallback chains for different models (up to 5 groups at a time)"})]})]})}),open:e,width:900,footer:null,onCancel:r,maskClosable:!1,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:(0,t.jsx)("div",{className:"mt-6",children:l})})}var G=e.i(419470);function H({accessToken:e,value:l=[],onChange:a}){let[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)(0),[g,m]=(0,r.useState)(!1),[p,f]=(0,r.useState)([{id:"1",primaryModel:null,fallbackModels:[]}]);(0,r.useEffect)(()=>{s&&(f([{id:"1",primaryModel:null,fallbackModels:[]}]),c(e=>e+1))},[s]),(0,r.useEffect)(()=>{let t=async()=>{try{let t=await (0,R.fetchAvailableModels)(e);o(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}};s&&t()},[e,s]);let h=Array.from(new Set(i.map(e=>e.model_group))).sort(),y=()=>{n(!1),f([{id:"1",primaryModel:null,fallbackModels:[]}])},x=async()=>{let e=p.filter(e=>!e.primaryModel||0===e.fallbackModels.length);if(e.length>0)return void E.default.error(`Please complete configuration for all groups. ${e.length} group(s) incomplete.`);let t=[...l||[],...p.map(e=>({[e.primaryModel]:e.fallbackModels}))];if(a){m(!0);try{await a(t),S.default.success(`${p.length} fallback configuration(s) added successfully!`),y()}catch(e){console.error("Error saving fallbacks:",e)}finally{m(!1)}}else S.default.fromBackend("onChange callback not provided")};return(0,t.jsxs)("div",{children:[(0,t.jsx)(u.Button,{className:"mx-auto",onClick:()=>n(!0),icon:()=>(0,t.jsx)("span",{className:"mr-1",children:"+"}),children:"Add Fallbacks"}),(0,t.jsxs)($,{open:s,onCancel:y,children:[(0,t.jsx)(G.FallbackSelectionForm,{groups:p,onGroupsChange:f,availableModels:h,maxFallbacks:10,maxGroups:5},d),p.length>0&&(0,t.jsxs)("div",{className:"flex items-center justify-end space-x-3 pt-6 mt-6 border-t border-gray-100",children:[(0,t.jsx)(C.Button,{type:"default",onClick:y,disabled:g,children:"Cancel"}),(0,t.jsx)(C.Button,{type:"default",onClick:x,disabled:0===p.length||g,loading:g,children:g?"Saving Configuration...":"Save All Configurations"})]})]})]})}let K="inline-flex items-center gap-2 px-2.5 py-1 rounded-md border border-gray-200 bg-gray-50 text-sm font-medium text-gray-800 shrink-0";async function U(e,r){console.log=function(){};let l=window.location.origin,a=new L.default.OpenAI({apiKey:r,baseURL:l,dangerouslyAllowBrowser:!0});try{S.default.info("Testing fallback model response...");let r=await a.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});S.default.success((0,t.jsxs)("span",{children:["Test model=",(0,t.jsx)("strong",{children:e}),", received model=",(0,t.jsx)("strong",{children:r.model}),". See"," ",(0,t.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){S.default.fromBackend(`Error occurred while generating model response. Please try again. Error: ${e}`)}}let q=({accessToken:e,userRole:l,userID:c})=>{let[u,m]=(0,r.useState)({}),[p,f]=(0,r.useState)(!1),[h,y]=(0,r.useState)(null),[x,_]=(0,r.useState)(!1),{data:w}=(0,T.useModelCostMap)(),C=e=>null!=w&&"object"==typeof w&&e in w?w[e].litellm_provider??"":"";(0,r.useEffect)(()=>{e&&l&&c&&(0,b.getCallbacksCall)(e,c,l).then(e=>{let t=e.router_settings;"model_group_retry_policy"in t&&delete t.model_group_retry_policy,m(t)})},[e,l,c]);let k=e=>{y(e),_(!0)},v=async()=>{if(!h||!e)return;let t=Object.keys(h)[0];if(!t)return;f(!0);let r=u.fallbacks.map(e=>{let r={...e};return t in r&&Array.isArray(r[t])&&delete r[t],r}).filter(e=>Object.keys(e).length>0),l={...u,fallbacks:r};try{await (0,b.setCallbacksCall)(e,{router_settings:l}),m(l),S.default.success("Router settings updated successfully")}catch(e){S.default.fromBackend("Failed to update router settings: "+e)}finally{f(!1),_(!1),y(null)}};if(!e)return null;let L=async t=>{if(!e)return;let r={...u,fallbacks:t};try{await (0,b.setCallbacksCall)(e,{router_settings:r}),m(r)}catch(t){throw S.default.fromBackend("Failed to update router settings: "+t),e&&l&&c&&(0,b.getCallbacksCall)(e,c,l).then(e=>{let t=e.router_settings;"model_group_retry_policy"in t&&delete t.model_group_retry_policy,m(t)}),t}},E=Array.isArray(u.fallbacks)&&u.fallbacks.length>0,R=(0,B.isProxyAdminRole)(l??"");return(0,t.jsxs)(t.Fragment,{children:[R&&(0,t.jsx)(H,{accessToken:e||"",value:u.fallbacks||[],onChange:L}),E?(0,t.jsxs)(a.Table,{children:[(0,t.jsx)(s.TableHead,{children:(0,t.jsxs)(n.TableRow,{children:[(0,t.jsx)(i.TableHeaderCell,{children:"Model Name"}),(0,t.jsx)(i.TableHeaderCell,{children:"Fallbacks"}),(0,t.jsx)(i.TableHeaderCell,{children:"Actions"})]})}),(0,t.jsx)(d.TableBody,{children:u.fallbacks.map((l,a)=>Object.entries(l).map(([s,i])=>{let d;return(0,t.jsxs)(n.TableRow,{children:[(0,t.jsx)(o.TableCell,{className:"align-top",children:(d=C?.(s)??s,(0,t.jsxs)("span",{className:K,children:[(0,t.jsx)(O.ProviderLogo,{provider:d,className:"w-4 h-4 shrink-0"}),(0,t.jsx)("span",{children:s})]}))}),(0,t.jsx)(o.TableCell,{className:"align-top",children:function(e,l){let a=Array.isArray(e)?e:[];if(0===a.length)return null;let s=({modelName:e})=>{let r=l?.(e)??e;return(0,t.jsxs)("span",{className:K,children:[(0,t.jsx)(O.ProviderLogo,{provider:r,className:"w-4 h-4 shrink-0"}),(0,t.jsx)("span",{children:e})]})};return(0,t.jsxs)("span",{className:"grid grid-cols-[auto_1fr] items-start gap-x-2 w-full min-w-0",children:[(0,t.jsx)("span",{className:"inline-flex items-center justify-center w-8 h-8 shrink-0 self-start text-blue-600","aria-hidden":!0,children:(0,t.jsx)(N,{className:"w-5 h-5 stroke-[2.5]"})}),(0,t.jsx)("span",{className:"flex flex-wrap items-start gap-1 min-w-0",children:a.map((e,l)=>(0,t.jsxs)(r.default.Fragment,{children:[l>0&&(0,t.jsx)(g.Icon,{icon:N,size:"xs",className:"shrink-0 text-gray-400"}),(0,t.jsx)(s,{modelName:e})]},e))})]})}(Array.isArray(i)?i:[],C)}),(0,t.jsx)(o.TableCell,{className:"align-top",children:R&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(I.Tooltip,{title:"Test fallback",children:(0,t.jsx)(g.Icon,{icon:A.PlayIcon,size:"sm",onClick:()=>U(Object.keys(l)[0],e||""),className:"cursor-pointer hover:text-blue-600"})}),(0,t.jsx)(I.Tooltip,{title:"Delete fallback",children:(0,t.jsx)("span",{"data-testid":"delete-fallback-button",role:"button",tabIndex:0,onClick:()=>k(l),onKeyDown:e=>"Enter"===e.key&&k(l),className:"cursor-pointer inline-flex",children:(0,t.jsx)(g.Icon,{icon:j.TrashIcon,size:"sm",className:"hover:text-red-600"})})})]})})]},a.toString()+s)}))})]}):(0,t.jsx)("div",{className:"rounded-lg border border-gray-200 bg-gray-50 px-4 py-6 text-center",children:(0,t.jsx)(F.Typography.Text,{type:"secondary",children:"No fallbacks configured. Add fallbacks to automatically try another model when the primary fails."})}),(0,t.jsx)(M.default,{isOpen:x,title:"Delete Fallback?",message:"Are you sure you want to delete this fallback? This action cannot be undone.",resourceInformationTitle:"Fallback Information",resourceInformation:[{label:"Model Name",value:h?Object.keys(h)[0]:"",code:!0}],onCancel:()=>{_(!1),y(null)},onOk:v,confirmLoading:p})]})};var z=e.i(175712),J=e.i(525720),Q=e.i(311451),Y=e.i(770914),V=e.i(646563),X=e.i(91979),W=e.i(928685),Z=e.i(135214),ee=e.i(954616),et=e.i(266027),er=e.i(912598),el=e.i(243652);let ea=(0,el.createQueryKeys)("routingGroups"),es=async e=>{let t=await (0,b.getRouterSettingsCall)(e),r=t?.current_values??{},l=(Array.isArray(t?.fields)?t.fields:[]).find(e=>e?.field_name==="routing_strategy");return{routingGroups:Array.isArray(r.routing_groups)?r.routing_groups:[],routingStrategy:r.routing_strategy??null,availableStrategies:Array.isArray(l?.options)?l.options:[]}},en=(0,el.createQueryKeys)("routerFields"),ei=async e=>{try{let t=b.proxyBaseUrl?`${b.proxyBaseUrl}/router/fields`:"/router/fields",r=await fetch(t,{method:"GET",headers:{[(0,b.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=e?.error&&(e.error.message||e.error)||e?.message||e?.detail||e?.error||JSON.stringify(e);throw Error(t)}return await r.json()}catch(e){throw console.error("Failed to fetch router fields:",e),e}};var eo=e.i(625901),ed=e.i(592392),ec=e.i(291542),eu=e.i(653496),eg=e.i(262218),em=e.i(539677),ep=e.i(955135),ef=e.i(751904),eh=e.i(245094);let{Text:ey,Paragraph:ex}=F.Typography,eb=e=>{switch(e){case"simple-shuffle":return"Simple Shuffle";case"least-busy":return"Least Busy";case"usage-based-routing":return"Usage Based";case"latency-based-routing":return"Latency Based";default:return e}},e_=e=>e.models[0]??"",ej={backgroundColor:"#111827",color:"#f3f4f6",borderRadius:6,padding:16,fontSize:12,whiteSpace:"pre",overflowX:"auto"},ew=({group:e,baseUrl:l})=>{let a={curl:`curl -X POST '${l}/v1/chat/completions' \\ + -H 'Content-Type: application/json' \\ + -H 'Authorization: Bearer $LITELLM_API_KEY' \\ + -d '{ + "model": "${e_(e)}", + "messages": [{"role": "user", "content": "Hello!"}] + }'`,python:`from openai import OpenAI + +client = OpenAI( + api_key="$LITELLM_API_KEY", + base_url="${l}", +) + +response = client.chat.completions.create( + model="${e_(e)}", + messages=[{"role": "user", "content": "Hello!"}], +) + +print(response)`,javascript:`import OpenAI from "openai"; + +const client = new OpenAI({ + apiKey: process.env.LITELLM_API_KEY, + baseURL: "${l}", +}); + +const response = await client.chat.completions.create({ + model: "${e_(e)}", + messages: [{ role: "user", content: "Hello!" }], +}); + +console.log(response);`},[s,n]=(0,r.useState)("curl"),i=[{key:"curl",label:"cURL"},{key:"python",label:"Python (OpenAI SDK)"},{key:"javascript",label:"JavaScript (OpenAI SDK)"}].map(({key:e,label:r})=>({key:e,label:r,children:(0,t.jsx)(ex,{code:!0,className:"mb-0!",style:ej,children:a[e]})}));return(0,t.jsx)(eu.Tabs,{size:"small",activeKey:s,onChange:e=>n(e),items:i,tabBarExtraContent:(0,t.jsx)(ex,{copyable:{text:a[s],tooltips:["Copy","Copied"]},className:"mb-0!"})})},eC=({groups:e,loading:l,onEdit:a,onDelete:s,proxyBaseUrl:n})=>{let[i,o]=(0,r.useState)([]),d=n&&n.trim()?n:window.location?.origin?window.location.origin:"",c=[{title:"GROUP NAME",dataIndex:"group_name",key:"group_name",render:e=>(0,t.jsx)(ey,{strong:!0,className:"text-blue-600",children:e})},{title:"MODELS",dataIndex:"models",key:"models",render:e=>(0,t.jsx)(J.Flex,{wrap:"wrap",gap:4,children:e.map(e=>(0,t.jsx)(eg.Tag,{children:e},e))})},{title:"STRATEGY",dataIndex:"routing_strategy",key:"routing_strategy",render:e=>(0,t.jsxs)("span",{className:"inline-flex items-center gap-1.5",children:[(0,t.jsx)(em.BranchesOutlined,{className:"text-gray-400"}),(0,t.jsx)(ey,{children:eb(e)})]})},{title:"ACTIONS",key:"actions",width:120,align:"right",render:(e,r)=>(0,t.jsxs)(J.Flex,{justify:"flex-end",align:"center",gap:8,children:[(0,t.jsx)(I.Tooltip,{title:"Edit",children:(0,t.jsx)(C.Button,{type:"text",icon:(0,t.jsx)(ef.EditOutlined,{}),onClick:e=>{e.stopPropagation(),a(r)}})}),(0,t.jsx)(I.Tooltip,{title:"Delete",children:(0,t.jsx)(C.Button,{type:"text",danger:!0,icon:(0,t.jsx)(ep.DeleteOutlined,{}),onClick:e=>{e.stopPropagation(),s(r)}})})]})}];return(0,t.jsx)(ec.Table,{rowKey:"group_name",columns:c,dataSource:e,loading:l,pagination:!1,expandable:{expandedRowKeys:i,onExpandedRowsChange:e=>o([...e]),expandedRowRender:e=>(0,t.jsxs)("div",{className:"bg-gray-50 border border-gray-200 rounded-md p-4 my-2",children:[(0,t.jsxs)(J.Flex,{align:"center",gap:8,className:"mb-2",children:[(0,t.jsx)(eh.CodeOutlined,{className:"text-blue-500"}),(0,t.jsx)(ey,{strong:!0,children:"How routing works for this group"})]}),(0,t.jsxs)(ex,{className:"text-sm text-gray-600 mb-3",children:["Callers request any model in the group by name — LiteLLM picks a deployment behind the scenes using the"," ",(0,t.jsx)(ey,{strong:!0,children:eb(e.routing_strategy)})," strategy."]}),(0,t.jsx)(ew,{group:e,baseUrl:d})]})}})};var eS=e.i(808613),ek=e.i(199133);let{Text:ev,Paragraph:eT}=F.Typography,eN=new Set(["latency-based-routing","usage-based-routing"]),eA=/^[A-Za-z0-9._-]+$/,eI=({open:e,mode:l,initialValue:a,availableStrategies:s,strategyDescriptions:n,modelOptions:i,existingGroupNames:o,onClose:d,onSubmit:c,saving:u})=>{let[g]=eS.Form.useForm(),m=eS.Form.useWatch("routing_strategy",g),p={group_name:a?.group_name??"",models:a?.models??[],routing_strategy:a?.routing_strategy??s[0]??"simple-shuffle",routing_strategy_args:a?.routing_strategy_args?JSON.stringify(a.routing_strategy_args,null,2):""},f=(0,r.useMemo)(()=>new Set(o.filter(e=>e!==a?.group_name).map(e=>e.toLowerCase())),[o,a]),h=async()=>{let e=await g.validateFields(),t=eN.has(String(e.routing_strategy)),r=null;if(t&&e.routing_strategy_args&&e.routing_strategy_args.trim())try{r=JSON.parse(e.routing_strategy_args)}catch{g.setFields([{name:"routing_strategy_args",errors:["Must be valid JSON"]}]);return}await c({group_name:e.group_name.trim(),models:e.models,routing_strategy:e.routing_strategy,routing_strategy_args:r})};return(0,t.jsx)(P.Modal,{title:"create"===l?"Create Routing Group":`Edit ${a?.group_name??""}`,open:e,onCancel:d,onOk:h,okText:"create"===l?"Create Group":"Save Changes",cancelText:"Cancel",confirmLoading:u,destroyOnClose:!0,width:560,children:(0,t.jsxs)(eS.Form,{form:g,layout:"vertical",preserve:!1,initialValues:p,children:[(0,t.jsx)(eS.Form.Item,{label:"Group Name",name:"group_name",rules:[{required:!0,message:"Group name is required"},{max:64,message:"Must be 64 characters or fewer"},{pattern:eA,message:"Only letters, numbers, dot, underscore, and dash are allowed"},{validator:(e,t)=>t&&f.has(t.trim().toLowerCase())?Promise.reject(Error("A group with this name already exists")):Promise.resolve()}],extra:"Use this name as the model in API calls — LiteLLM routes the request to one of the group's models.",children:(0,t.jsx)(Q.Input,{placeholder:"fast-chat",disabled:"edit"===l})}),(0,t.jsx)(eS.Form.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Select at least one model"}],extra:"Models from your model list that this group routes between.",children:(0,t.jsx)(ek.Select,{mode:"multiple",allowClear:!0,placeholder:"Select models",options:i.map(e=>({label:e,value:e})),optionFilterProp:"label"})}),(0,t.jsx)(eS.Form.Item,{label:"Routing Strategy",name:"routing_strategy",rules:[{required:!0,message:"Strategy is required"}],children:(0,t.jsx)(ek.Select,{options:s.map(e=>({label:e,value:e})),placeholder:"Select strategy"})}),m&&n[m]&&(0,t.jsx)(eT,{className:"text-xs text-gray-500 -mt-2 mb-4",children:n[m]}),eN.has(String(m))&&(0,t.jsx)(eS.Form.Item,{label:"Strategy Arguments (JSON)",name:"routing_strategy_args",extra:"latency-based-routing"===m?'Example: { "ttl": 3600, "lowest_latency_buffer": 0 }':'Example: { "ttl": 60 }',children:(0,t.jsx)(Q.Input.TextArea,{rows:4,placeholder:'{ "ttl": 3600 }',className:"font-mono text-xs"})}),(0,t.jsx)(Y.Space,{direction:"vertical",className:"w-full mt-2",children:(0,t.jsx)(ev,{type:"secondary",className:"text-xs",children:"Models not claimed by an explicit group fall through to the proxy's top-level routing strategy."})})]},"edit"===l?`edit-${a?.group_name??""}`:"create")})},{Text:eF}=F.Typography,eL=()=>{let{data:e,isLoading:l,refetch:a,isFetching:s}=(()=>{let{accessToken:e,userId:t,userRole:r}=(0,Z.default)();return(0,et.useQuery)({queryKey:ea.lists(),queryFn:()=>es(e),enabled:!!(e&&t&&r)})})(),{data:n}=(()=>{let{accessToken:e,userId:t,userRole:r}=(0,Z.default)();return(0,et.useQuery)({queryKey:en.detail("fields"),queryFn:async()=>await ei(e),enabled:!!(e&&t&&r)})})(),{data:i}=(0,eo.useModelHub)(),{accessToken:o}=(0,Z.default)(),d=(0,ed.default)(o),c=(()=>{let{accessToken:e}=(0,Z.default)(),t=(0,er.useQueryClient)();return(0,ee.useMutation)({mutationFn:t=>(0,b.setCallbacksCall)(e,{router_settings:{routing_groups:t}}),onSuccess:()=>{t.invalidateQueries({queryKey:ea.lists()})}})})(),[u,g]=(0,r.useState)(""),[m,p]=(0,r.useState)(!1),[f,h]=(0,r.useState)("create"),[y,x]=(0,r.useState)(null),[_,j]=(0,r.useState)(null),w=e?.routingGroups??[],k=(0,r.useMemo)(()=>{let e=u.trim().toLowerCase();return e?w.filter(t=>t.group_name.toLowerCase().includes(e)||t.routing_strategy.toLowerCase().includes(e)||t.models.some(t=>t.toLowerCase().includes(e))):w},[w,u]),v=(0,r.useMemo)(()=>e?.availableStrategies?.length?e.availableStrategies:n?.fields?.find(e=>"routing_strategy"===e.field_name)?.options??[],[e?.availableStrategies,n]),T=n?.routing_strategy_descriptions??{},N=(0,r.useMemo)(()=>Array.from(new Set((i?.data??[]).map(e=>e.model_group).filter(e=>!!e))),[i]),A=async e=>{let t="create"===f?[...w,e]:w.map(t=>t.group_name===y?.group_name?e:t);try{await c.mutateAsync(t),S.default.success("create"===f?`Created routing group "${e.group_name}"`:`Updated routing group "${e.group_name}"`),p(!1)}catch(e){S.default.error(e instanceof Error?e.message:"Failed to save routing group")}},I=async()=>{if(!_)return;let e=w.filter(e=>e.group_name!==_.group_name);try{await c.mutateAsync(e),S.default.success(`Deleted routing group "${_.group_name}"`),j(null)}catch(e){S.default.error(e instanceof Error?e.message:"Failed to delete routing group")}};return(0,t.jsxs)(Y.Space,{direction:"vertical",size:16,className:"w-full",children:[(0,t.jsxs)(z.Card,{bodyStyle:{padding:16},children:[(0,t.jsxs)(J.Flex,{justify:"space-between",align:"center",gap:12,className:"mb-4",children:[(0,t.jsx)(Q.Input,{allowClear:!0,prefix:(0,t.jsx)(W.SearchOutlined,{className:"text-gray-400"}),placeholder:"Search groups...",value:u,onChange:e=>g(e.target.value),className:"max-w-sm"}),(0,t.jsxs)(J.Flex,{align:"center",gap:12,children:[(0,t.jsx)(C.Button,{icon:(0,t.jsx)(X.ReloadOutlined,{}),onClick:()=>a(),loading:s&&!l,children:"Refresh"}),(0,t.jsx)(C.Button,{type:"primary",icon:(0,t.jsx)(V.PlusOutlined,{}),onClick:()=>{h("create"),x(null),p(!0)},children:"Create Group"}),(0,t.jsxs)(eF,{type:"secondary",className:"text-sm whitespace-nowrap",children:["Showing ",k.length," ",1===k.length?"result":"results"]})]})]}),(0,t.jsx)(eC,{groups:k,loading:l,onEdit:e=>{h("edit"),x(e),p(!0)},onDelete:e=>j(e),proxyBaseUrl:d.LITELLM_UI_API_DOC_BASE_URL?.trim()||d.PROXY_BASE_URL||""})]}),(0,t.jsx)(eI,{open:m,mode:f,initialValue:y,availableStrategies:v,strategyDescriptions:T,modelOptions:N,existingGroupNames:w.map(e=>e.group_name),onClose:()=>p(!1),onSubmit:A,saving:c.isPending}),(0,t.jsx)(P.Modal,{open:!!_,title:"Delete routing group?",okText:"Delete",okButtonProps:{danger:!0,loading:c.isPending},cancelText:"Cancel",onOk:I,onCancel:()=>j(null),children:(0,t.jsxs)(eF,{children:["Models in ",(0,t.jsx)(eF,{strong:!0,children:_?.group_name}),"will fall back to the proxy's top-level routing strategy. This cannot be undone."]})})]})},eM=({accessToken:e,userRole:C,userID:S})=>{let[k,T]=(0,r.useState)([]);(0,r.useEffect)(()=>{e&&(0,b.getGeneralSettingsCall)(e).then(e=>{T(e)})},[e]);let N=(e,t)=>{T(k.map(r=>r.field_name===e?{...r,field_value:t}:r))};return e?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(h.TabGroup,{className:"h-[75vh] w-full",children:[(0,t.jsxs)(y.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(x.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(x.Tab,{value:"2",children:"Routing Groups"}),(0,t.jsx)(x.Tab,{value:"3",children:"Fallbacks"}),(0,t.jsx)(x.Tab,{value:"4",children:"General"})]}),(0,t.jsxs)(f.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(v,{accessToken:e,userRole:C,userID:S})}),(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(eL,{})}),(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(q,{accessToken:e,userRole:C,userID:S})}),(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(l.Card,{children:(0,t.jsxs)(a.Table,{children:[(0,t.jsx)(s.TableHead,{children:(0,t.jsxs)(n.TableRow,{children:[(0,t.jsx)(i.TableHeaderCell,{children:"Setting"}),(0,t.jsx)(i.TableHeaderCell,{children:"Value"}),(0,t.jsx)(i.TableHeaderCell,{children:"Status"}),(0,t.jsx)(i.TableHeaderCell,{children:"Action"})]})}),(0,t.jsx)(d.TableBody,{children:k.filter(e=>"TypedDictionary"!==e.field_type).map((r,l)=>(0,t.jsxs)(n.TableRow,{children:[(0,t.jsxs)(o.TableCell,{children:[(0,t.jsx)(c.Text,{children:r.field_name}),(0,t.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:r.field_description})]}),(0,t.jsx)(o.TableCell,{children:"Integer"==r.field_type?(0,t.jsx)(_.InputNumber,{step:1,value:r.field_value,onChange:e=>N(r.field_name,e)}):"Boolean"==r.field_type?(0,t.jsx)(m.Switch,{checked:!0===r.field_value||"true"===r.field_value,onChange:e=>N(r.field_name,e)}):"Float"==r.field_type?(0,t.jsx)(_.InputNumber,{min:0,max:1,step:.05,value:r.field_value,onChange:e=>N(r.field_name,e)}):null}),(0,t.jsx)(o.TableCell,{children:!0==r.stored_in_db?(0,t.jsx)(w.StatusBadge,{tone:"success",label:"In DB"}):!1==r.stored_in_db?(0,t.jsx)(w.StatusBadge,{tone:"neutral",label:"In Config"}):(0,t.jsx)(w.StatusBadge,{tone:"neutral",label:"Not Set"})}),(0,t.jsxs)(o.TableCell,{children:[(0,t.jsx)(u.Button,{onClick:()=>((t,r)=>{if(!e)return;let l=k[r].field_value;if(null!=l&&void 0!=l)try{(0,b.updateConfigFieldSetting)(e,t,l);let r=k.map(e=>e.field_name===t?{...e,stored_in_db:!0}:e);T(r)}catch(e){}})(r.field_name,l),children:"Update"}),(0,t.jsx)(g.Icon,{icon:j.TrashIcon,color:"red",onClick:()=>(t=>{if(e)try{(0,b.deleteConfigFieldSetting)(e,t);let r=k.map(e=>e.field_name===t?{...e,stored_in_db:null,field_value:null}:e);T(r)}catch(e){}})(r.field_name),children:"Reset"})]})]},l))})]})})})]})]})}):null};e.s(["default",0,function(){let{accessToken:e,userRole:r,userId:l}=(0,Z.default)();return(0,t.jsx)(eM,{userID:l,userRole:r,accessToken:e})}],389543)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/04119inby~4wy.js b/litellm/proxy/_experimental/out/_next/static/chunks/04119inby~4wy.js new file mode 100644 index 00000000000..a6ebdc274cc --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/04119inby~4wy.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let n=l(e);t(n),r.current=n,o&&o({current:n})};var s=e.i(480731),d=e.i(444755),c=e.i(673706);let g=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let u={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},b=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},p=(0,c.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:l,transitionStatus:n})=>{let i=l?r===s.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(g,{className:(0,d.tremorTwMerge)(p("icon"),"animate-spin shrink-0",i,m.default,m[n]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(p("icon"),"shrink-0",t,i)})},f=a.default.forwardRef((e,o)=>{let{icon:g,iconPosition:m=s.HorizontalPositions.Left,size:f=s.Sizes.SM,color:x,variant:C="primary",disabled:k,loading:w=!1,loadingText:v,children:N,tooltip:$,className:y}=e,j=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),T=w||k,B=void 0!==g||w,E=w&&v,O=!(!N&&!E),S=(0,d.tremorTwMerge)(u[f].height,u[f].width),M="light"!==C?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=b(C,x),z=("light"!==C?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[f],{tooltipProps:P,getReferenceProps:H}=(0,r.useTooltip)(300),[q,F]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:s,initialEntered:d,mountOnEnter:c,unmountOnExit:g,onStateChange:m}={})=>{let[u,b]=(0,a.useState)(()=>l(d?2:n(c))),p=(0,a.useRef)(u),h=(0,a.useRef)(0),[f,x]="object"==typeof s?[s.enter,s.exit]:[s,s],C=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(p.current._s,g);e&&i(e,b,p,h,m)},[m,g]);return[u,(0,a.useCallback)(a=>{let l=e=>{switch(i(e,b,p,h,m),e){case 1:f>=0&&(h.current=((...e)=>setTimeout(...e))(C,f));break;case 4:x>=0&&(h.current=((...e)=>setTimeout(...e))(C,x));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},s=p.current.isEnter;"boolean"!=typeof a&&(a=!s),a?s||l(e?+!r:2):s&&l(t?o?3:4:n(g))},[C,m,e,t,r,o,f,x,g]),C]})({timeout:50});return(0,a.useEffect)(()=>{F(w)},[w]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([o,P.refs.setReference]),className:(0,d.tremorTwMerge)(p("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",M,z.paddingX,z.paddingY,z.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,T?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(b(C,x).hoverTextColor,b(C,x).hoverBgColor,b(C,x).hoverBorderColor),y),disabled:T},H,j),a.default.createElement(r.default,Object.assign({text:$},P)),B&&m!==s.HorizontalPositions.Right?a.default.createElement(h,{loading:w,iconSize:S,iconPosition:m,Icon:g,transitionStatus:q.status,needMargin:O}):null,E||N?a.default.createElement("span",{className:(0,d.tremorTwMerge)(p("text"),"text-tremor-default whitespace-nowrap")},E?v:N):null,B&&m===s.HorizontalPositions.Right?a.default.createElement(h,{loading:w,iconSize:S,iconPosition:m,Icon:g,transitionStatus:q.status,needMargin:O}):null)});f.displayName="Button",e.s(["Button",0,f],994388)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),l=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:d="",decorationColor:c,children:g,className:m}=e,u=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,n.getColorClassNames)(c,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},u),g)});s.displayName="Card",e.s(["Card",0,s],304967)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),o=e.i(529681);let l=e=>{let{prefixCls:a,className:o,style:l,size:n,shape:i}=e,s=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),d=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),c=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,s,d,o),style:Object.assign(Object.assign({},c),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),g=e=>({height:e,lineHeight:(0,i.unit)(e)}),m=e=>Object.assign({width:e},g(e)),u=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},g(e)),b=e=>Object.assign({width:e},g(e)),p=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},g(e)),f=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:o,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:s,controlHeightLG:d,controlHeightSM:g,gradientFromColor:f,padding:x,marginSM:C,borderRadius:k,titleHeight:w,blockRadius:v,paragraphLiHeight:N,controlHeightXS:$,paragraphMarginTop:y}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:x,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},m(s)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},m(d)),[`${r}-sm`]:Object.assign({},m(g))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:f,borderRadius:v,[`+ ${o}`]:{marginBlockStart:g}},[o]:{padding:0,"> li":{width:"100%",height:N,listStyle:"none",background:f,borderRadius:v,"+ li":{marginBlockStart:$}}},[`${o}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${o} > li`]:{borderRadius:k}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:C,[`+ ${o}`]:{marginBlockStart:y}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},p(e,a,r)),{[`${r}-lg`]:Object.assign({},h(o,i))}),p(e,o,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),p(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},m(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},m(o)),[`${t}${t}-sm`]:Object.assign({},m(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},u(t,i)),[`${a}-lg`]:Object.assign({},u(o,i)),[`${a}-sm`]:Object.assign({},u(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:o,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:o},b(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${o} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),x=e=>{let{prefixCls:a,className:o,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,o),style:l},i)},C=({prefixCls:e,className:a,width:o,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:o},l)});function k(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:o,loading:n,className:i,rootClassName:s,style:d,children:c,avatar:g=!1,title:m=!0,paragraph:u=!0,active:b,round:p}=e,{getPrefixCls:h,direction:w,className:v,style:N}=(0,a.useComponentConfig)("skeleton"),$=h("skeleton",o),[y,j,T]=f($);if(n||!("loading"in e)){let e,a,o=!!g,n=!!m,c=!!u;if(o){let r=Object.assign(Object.assign({prefixCls:`${$}-avatar`},n&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),k(g));e=t.createElement("div",{className:`${$}-header`},t.createElement(l,Object.assign({},r)))}if(n||c){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${$}-title`},!o&&c?{width:"38%"}:o&&c?{width:"50%"}:{}),k(m));e=t.createElement(C,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${$}-paragraph`},(e={},o&&n||(e.width="61%"),!o&&n?e.rows=3:e.rows=2,e)),k(u));r=t.createElement(x,Object.assign({},a))}a=t.createElement("div",{className:`${$}-content`},e,r)}let h=(0,r.default)($,{[`${$}-with-avatar`]:o,[`${$}-active`]:b,[`${$}-rtl`]:"rtl"===w,[`${$}-round`]:p},v,i,s,j,T);return y(t.createElement("div",{className:h,style:Object.assign(Object.assign({},N),d)},e,a))}return null!=c?c:null};w.Button=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c=!1,size:g="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),u=m("skeleton",n),[b,p,h]=f(u),x=(0,o.default)(e,["prefixCls"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d,[`${u}-block`]:c},i,s,p,h);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-button`,size:g},x))))},w.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,shape:c="circle",size:g="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),u=m("skeleton",n),[b,p,h]=f(u),x=(0,o.default)(e,["prefixCls","className"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d},i,s,p,h);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-avatar`,shape:c,size:g},x))))},w.Input=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c,size:g="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),u=m("skeleton",n),[b,p,h]=f(u),x=(0,o.default)(e,["prefixCls"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d,[`${u}-block`]:c},i,s,p,h);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-input`,size:g},x))))},w.Image=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",o),[g,m,u]=f(c),b=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:s},l,n,m,u);return g(t.createElement("div",{className:b},t.createElement("div",{className:(0,r.default)(`${c}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},w.Node=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),g=c("skeleton",o),[m,u,b]=f(g),p=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:s},u,l,n,b);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${g}-image`,l),style:i},d)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let o=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(o),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let o={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",o);let l=e<0?"-":"",n=Math.abs(e),i=n,s="";return n>=1e6?(i=n/1e6,s="M"):n>=1e3&&(i=n/1e3,s="K"),`${l}${i.toLocaleString("en-US",o)}${s}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return o(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),o(e,r)}},o=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let o=document.execCommand("copy");if(document.body.removeChild(a),o)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),o=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(o.TooltipProvider,{delay:300,children:(0,t.jsxs)(o.Tooltip,{children:[(0,t.jsx)(o.TooltipTrigger,{render:r}),(0,t.jsx)(o.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:o,tooltip:i,dataTestId:s}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":s,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:o});return i?(0,t.jsx)(l,{content:i,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`,`${s}, ${d} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:o,copyable:d=!1,truncate:c=!0,fallback:g="-",tooltip:m,disabled:u=!1,dataTestId:b,className:p}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:g});let h=!!o&&!u,f=(0,n.cn)(s[a].base,h&&s[a].clickable,c&&"block max-w-[15ch] truncate",u&&"opacity-50",p),x=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":b,onClick:()=>o(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":b,children:e}),C=(0,r.jsx)(t.CellTooltip,{content:m??e,trigger:x});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[C,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):C}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:o=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?o?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},s),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",i)},s),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},s),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},s),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),i)},s),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),o=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,i.makeClassName)("Badge"),g=r.default.forwardRef((e,g)=>{let{color:m,icon:u,size:b=o.Sizes.SM,tooltip:p,className:h,children:f}=e,x=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),C=u||null,{tooltipProps:k,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([g,k.refs.setReference]),className:(0,n.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",m?(0,n.tremorTwMerge)((0,i.getColorClassNames)(m,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(m,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(m,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[b].paddingX,s[b].paddingY,s[b].fontSize,h)},w,x),r.default.createElement(a.default,Object.assign({text:p},k)),C?r.default.createElement(C,{className:(0,n.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[b].height,d[b].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(c("text"),"whitespace-nowrap")},f))});g.displayName="Badge",e.s(["Badge",0,g],389083)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/041lbypm7ppd8.js b/litellm/proxy/_experimental/out/_next/static/chunks/041lbypm7ppd8.js deleted file mode 100644 index 13e6ee51abf..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/041lbypm7ppd8.js +++ /dev/null @@ -1 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,726330,e=>{"use strict";var t,n=e.i(271645),r=e.i(981140),o=e.i(248425),a=e.i(820783),i=e.i(30207),s=e.i(683986),u=e.i(843476),c="dismissableLayer.update",l=n.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set,dismissableSurfaces:new Set}),d=n.forwardRef((e,d)=>{let{disableOutsidePointerEvents:p=!1,deferPointerDownOutside:m=!1,onEscapeKeyDown:h,onPointerDownOutside:g,onFocusOutside:E,onInteractOutside:y,onDismiss:b,...w}=e,C=n.useContext(l),[R,D]=n.useState(null),S=R?.ownerDocument??globalThis?.document,[,P]=n.useState({}),L=(0,a.useComposedRefs)(d,D),x=Array.from(C.layers),[T]=[...C.layersWithOutsidePointerEventsDisabled].slice(-1),_=x.indexOf(T),k=R?x.indexOf(R):-1,N=C.layersWithOutsidePointerEventsDisabled.size>0,O=k>=_,F=n.useRef(!1),M=function(e,t){let{ownerDocument:r=globalThis?.document,deferPointerDownOutside:o=!1,isDeferredPointerDownOutsideRef:a,dismissableSurfaces:s}=t,u=(0,i.useCallbackRef)(e),c=n.useRef(!1),l=n.useRef(!1),d=n.useRef(new Map),f=n.useRef(()=>{});return n.useEffect(()=>{function e(){l.current=!1,a.current=!1,d.current.clear()}function t(e){if(!l.current)return;let t=e.target;t instanceof Node&&[...s].some(e=>e.contains(t))||d.current.set(e.type,!0),"click"===e.type&&window.setTimeout(()=>{l.current&&f.current()},0)}function n(e){l.current&&d.current.set(e.type,!1)}let i=t=>{if(t.target&&!c.current){let n=function(){r.removeEventListener("click",f.current);let t=Array.from(d.current.values()).some(Boolean);e(),t||v("dismissableLayer.pointerDownOutside",u,i,{discrete:!0})},i={originalEvent:t};l.current=!0,a.current=o&&0===t.button,d.current.clear(),o&&0===t.button?(r.removeEventListener("click",f.current),f.current=n,r.addEventListener("click",f.current,{once:!0})):n()}else r.removeEventListener("click",f.current),e();c.current=!1},p=["pointerup","mousedown","mouseup","touchstart","touchend","click"];for(let e of p)r.addEventListener(e,t,!0),r.addEventListener(e,n);let m=window.setTimeout(()=>{r.addEventListener("pointerdown",i)},0);return()=>{for(let e of(window.clearTimeout(m),r.removeEventListener("pointerdown",i),r.removeEventListener("click",f.current),p))r.removeEventListener(e,t,!0),r.removeEventListener(e,n)}},[r,u,o,a,s]),{onPointerDownCapture:()=>c.current=!0}}(e=>{let t=e.target;if(!(t instanceof Node))return;let n=[...C.branches].some(e=>e.contains(t));O&&!n&&(g?.(e),y?.(e),e.defaultPrevented||b?.())},{ownerDocument:S,deferPointerDownOutside:m,isDeferredPointerDownOutsideRef:F,dismissableSurfaces:C.dismissableSurfaces}),I=function(e,t=globalThis?.document){let r=(0,i.useCallbackRef)(e),o=n.useRef(!1);return n.useEffect(()=>{let e=e=>{e.target&&!o.current&&v("dismissableLayer.focusOutside",r,{originalEvent:e},{discrete:!1})};return t.addEventListener("focusin",e),()=>t.removeEventListener("focusin",e)},[t,r]),{onFocusCapture:()=>o.current=!0,onBlurCapture:()=>o.current=!1}}(e=>{if(m&&F.current)return;let t=e.target;![...C.branches].some(e=>e.contains(t))&&(E?.(e),y?.(e),e.defaultPrevented||b?.())},S),j=!!R&&k===x.length-1,A=(0,s.useEffectEvent)(e=>{"Escape"===e.key&&(h?.(e),!e.defaultPrevented&&b&&(e.preventDefault(),b()))});return n.useEffect(()=>{if(j)return S.addEventListener("keydown",A,{capture:!0}),()=>S.removeEventListener("keydown",A,{capture:!0})},[S,j]),n.useEffect(()=>{if(R)return p&&(0===C.layersWithOutsidePointerEventsDisabled.size&&(t=S.body.style.pointerEvents,S.body.style.pointerEvents="none"),C.layersWithOutsidePointerEventsDisabled.add(R)),C.layers.add(R),f(),()=>{p&&(C.layersWithOutsidePointerEventsDisabled.delete(R),0===C.layersWithOutsidePointerEventsDisabled.size&&(S.body.style.pointerEvents=t))}},[R,S,p,C]),n.useEffect(()=>()=>{R&&(C.layers.delete(R),C.layersWithOutsidePointerEventsDisabled.delete(R),f())},[R,C]),n.useEffect(()=>{let e=()=>P({});return document.addEventListener(c,e),()=>document.removeEventListener(c,e)},[]),(0,u.jsx)(o.Primitive.div,{...w,ref:L,style:{pointerEvents:N?O?"auto":"none":void 0,...e.style},onFocusCapture:(0,r.composeEventHandlers)(e.onFocusCapture,I.onFocusCapture),onBlurCapture:(0,r.composeEventHandlers)(e.onBlurCapture,I.onBlurCapture),onPointerDownCapture:(0,r.composeEventHandlers)(e.onPointerDownCapture,M.onPointerDownCapture)})});function f(){let e=new CustomEvent(c);document.dispatchEvent(e)}function v(e,t,n,{discrete:r}){let a=n.originalEvent.target,i=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&a.addEventListener(e,t,{once:!0}),r?(0,o.dispatchDiscreteCustomEvent)(a,i):a.dispatchEvent(i)}d.displayName="DismissableLayer",n.forwardRef((e,t)=>{let r=n.useContext(l),i=n.useRef(null),s=(0,a.useComposedRefs)(t,i);return n.useEffect(()=>{let e=i.current;if(e)return r.branches.add(e),()=>{r.branches.delete(e)}},[r.branches]),(0,u.jsx)(o.Primitive.div,{...e,ref:s})}).displayName="DismissableLayerBranch",e.s(["DismissableLayer",0,d,"useDismissableLayerSurface",0,function(){let e=n.useContext(l),[t,r]=n.useState(null);return n.useEffect(()=>{if(t)return e.dismissableSurfaces.add(t),()=>{e.dismissableSurfaces.delete(t)}},[t,e.dismissableSurfaces]),r}])},765491,774606,e=>{"use strict";let t;var n=e.i(271645),r=e.i(820783),o=e.i(248425),a=e.i(30207),i=e.i(843476),s="focusScope.autoFocusOnMount",u="focusScope.autoFocusOnUnmount",c={bubbles:!1,cancelable:!0},l=n.forwardRef((e,t)=>{let{loop:l=!1,trapped:m=!1,onMountAutoFocus:h,onUnmountAutoFocus:g,...E}=e,[y,b]=n.useState(null),w=(0,a.useCallbackRef)(h),C=(0,a.useCallbackRef)(g),R=n.useRef(null),D=(0,r.useComposedRefs)(t,b),S=n.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;n.useEffect(()=>{if(m){let e=function(e){if(S.paused||!y)return;let t=e.target;y.contains(t)?R.current=t:v(R.current,{select:!0})},t=function(e){if(S.paused||!y)return;let t=e.relatedTarget;null!==t&&(y.contains(t)||v(R.current,{select:!0}))};document.addEventListener("focusin",e),document.addEventListener("focusout",t);let n=new MutationObserver(function(e){if(document.activeElement===document.body)for(let t of e)t.removedNodes.length>0&&v(y)});return y&&n.observe(y,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",e),document.removeEventListener("focusout",t),n.disconnect()}}},[m,y,S.paused]),n.useEffect(()=>{if(y){p.add(S);let e=document.activeElement;if(!y.contains(e)){let t=new CustomEvent(s,c);y.addEventListener(s,w),y.dispatchEvent(t),t.defaultPrevented||(function(e,{select:t=!1}={}){let n=document.activeElement;for(let r of e)if(v(r,{select:t}),document.activeElement!==n)return}(d(y).filter(e=>"A"!==e.tagName),{select:!0}),document.activeElement===e&&v(y))}return()=>{y.removeEventListener(s,w),setTimeout(()=>{let t=new CustomEvent(u,c);y.addEventListener(u,C),y.dispatchEvent(t),t.defaultPrevented||v(e??document.body,{select:!0}),y.removeEventListener(u,C),p.remove(S)},0)}}},[y,w,C,S]);let P=n.useCallback(e=>{if(!l&&!m||S.paused)return;let t="Tab"===e.key&&!e.altKey&&!e.ctrlKey&&!e.metaKey,n=document.activeElement;if(t&&n){var r;let t,o=e.currentTarget,[a,i]=[f(t=d(r=o),r),f(t.reverse(),r)];a&&i?e.shiftKey||n!==i?e.shiftKey&&n===a&&(e.preventDefault(),l&&v(i,{select:!0})):(e.preventDefault(),l&&v(a,{select:!0})):n===o&&e.preventDefault()}},[l,m,S.paused]);return(0,i.jsx)(o.Primitive.div,{tabIndex:-1,...E,ref:D,onKeyDown:P})});function d(e){let t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:e=>{let t="INPUT"===e.tagName&&"hidden"===e.type;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function f(e,t){for(let n of e)if(!function(e,{upTo:t}){if("hidden"===getComputedStyle(e).visibility)return!0;for(;e&&(void 0===t||e!==t);){if("none"===getComputedStyle(e).display)return!0;e=e.parentElement}return!1}(n,{upTo:t}))return n}function v(e,{select:t=!1}={}){if(e&&e.focus){var n;let r=document.activeElement;e.focus({preventScroll:!0}),e!==r&&(n=e)instanceof HTMLInputElement&&"select"in n&&t&&e.select()}}l.displayName="FocusScope";var p=(t=[],{add(e){let n=t[0];e!==n&&n?.pause(),(t=m(t,e)).unshift(e)},remove(e){t=m(t,e),t[0]?.resume()}});function m(e,t){let n=[...e],r=n.indexOf(t);return -1!==r&&n.splice(r,1),n}e.s(["FocusScope",0,l],765491);var h=e.i(174080),g=e.i(934620),E=n.forwardRef((e,t)=>{let{container:r,...a}=e,[s,u]=n.useState(!1);(0,g.useLayoutEffect)(()=>u(!0),[]);let c=r||s&&globalThis?.document?.body;return c?h.createPortal((0,i.jsx)(o.Primitive.div,{...a,ref:t}),c):null});E.displayName="Portal",e.s(["Portal",0,E],774606)},303536,e=>{"use strict";var t=e.i(271645),n=0,r=null;function o(){let e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}e.s(["useFocusGuards",0,function(){t.useEffect(()=>{r||(r={start:o(),end:o()});let{start:e,end:t}=r;return document.body.firstElementChild!==e&&document.body.insertAdjacentElement("afterbegin",e),document.body.lastElementChild!==t&&document.body.insertAdjacentElement("beforeend",t),n++,()=>{1===n&&(r?.start.remove(),r?.end.remove(),r=null),n=Math.max(0,n-1)}},[])}])},326999,985369,e=>{"use strict";var t,n,r,o,a,i,s,u=e.i(271645),c=e.i(981140),l=e.i(820783),d=e.i(30030),f=e.i(610772),v=e.i(369340),p=e.i(726330),m=e.i(765491),h=e.i(774606),g=e.i(296626),E=e.i(248425),y=e.i(303536),b=e.i(290571),w="right-scroll-bar-position",C="width-before-scroll-bar";function R(e,t){return"function"==typeof e?e(t):e&&(e.current=t),e}var D="u">typeof window?u.useLayoutEffect:u.useEffect,S=new WeakMap,P=(void 0===t&&(t={}),(void 0===n&&(n=function(e){return e}),r=[],o=!1,a={read:function(){if(o)throw Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return r.length?r[r.length-1]:null},useMedium:function(e){var t=n(e,o);return r.push(t),function(){r=r.filter(function(e){return e!==t})}},assignSyncMedium:function(e){for(o=!0;r.length;){var t=r;r=[],t.forEach(e)}r={push:function(t){return e(t)},filter:function(){return r}}},assignMedium:function(e){o=!0;var t=[];if(r.length){var n=r;r=[],n.forEach(e),t=r}var a=function(){var n=t;t=[],n.forEach(e)},i=function(){return Promise.resolve().then(a)};i(),r={push:function(e){t.push(e),i()},filter:function(e){return t=t.filter(e),r}}}}).options=(0,b.__assign)({async:!0,ssr:!1},t),a),L=function(){},x=u.forwardRef(function(e,t){var n,r,o,a,i=u.useRef(null),s=u.useState({onScrollCapture:L,onWheelCapture:L,onTouchMoveCapture:L}),c=s[0],l=s[1],d=e.forwardProps,f=e.children,v=e.className,p=e.removeScrollBar,m=e.enabled,h=e.shards,g=e.sideCar,E=e.noRelative,y=e.noIsolation,w=e.inert,C=e.allowPinchZoom,x=e.as,T=e.gapMode,_=(0,b.__rest)(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noRelative","noIsolation","inert","allowPinchZoom","as","gapMode"]),k=(n=[i,t],r=function(e){return n.forEach(function(t){return R(t,e)})},(o=(0,u.useState)(function(){return{value:null,callback:r,facade:{get current(){return o.value},set current(value){var e=o.value;e!==value&&(o.value=value,o.callback(value,e))}}}})[0]).callback=r,a=o.facade,D(function(){var e=S.get(a);if(e){var t=new Set(e),r=new Set(n),o=a.current;t.forEach(function(e){r.has(e)||R(e,null)}),r.forEach(function(e){t.has(e)||R(e,o)})}S.set(a,n)},[n]),a),N=(0,b.__assign)((0,b.__assign)({},_),c);return u.createElement(u.Fragment,null,m&&u.createElement(g,{sideCar:P,removeScrollBar:p,shards:h,noRelative:E,noIsolation:y,inert:w,setCallbacks:l,allowPinchZoom:!!C,lockRef:i,gapMode:T}),d?u.cloneElement(u.Children.only(f),(0,b.__assign)((0,b.__assign)({},N),{ref:k})):u.createElement(void 0===x?"div":x,(0,b.__assign)({},N,{className:v,ref:k}),f))});x.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},x.classNames={fullWidth:C,zeroRight:w};var T=function(e){var t=e.sideCar,n=(0,b.__rest)(e,["sideCar"]);if(!t)throw Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw Error("Sidecar medium not found");return u.createElement(r,(0,b.__assign)({},n))};T.isSideCarExport=!0;var _=function(){var e=0,t=null;return{add:function(n){if(0==e&&(t=function(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=s||("u">typeof __webpack_nonce__?__webpack_nonce__:void 0);return t&&e.setAttribute("nonce",t),e}())){var r,o;(r=t).styleSheet?r.styleSheet.cssText=n:r.appendChild(document.createTextNode(n)),o=t,(document.head||document.getElementsByTagName("head")[0]).appendChild(o)}e++},remove:function(){--e||!t||(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},k=function(){var e=_();return function(t,n){u.useEffect(function(){return e.add(t),function(){e.remove()}},[t&&n])}},N=function(){var e=k();return function(t){return e(t.styles,t.dynamic),null}},O={left:0,top:0,right:0,gap:0},F=function(e){return parseInt(e||"",10)||0},M=function(e){var t=window.getComputedStyle(document.body),n=t["padding"===e?"paddingLeft":"marginLeft"],r=t["padding"===e?"paddingTop":"marginTop"],o=t["padding"===e?"paddingRight":"marginRight"];return[F(n),F(r),F(o)]},I=function(e){if(void 0===e&&(e="margin"),"u"
- `;t.document.write(a),t.document.close(),t.onload=()=>{t.print()}})(e),a(!1)},children:[(0,t.jsx)(e_.FilePdfOutlined,{className:"mr-3 text-red-500"}),"Export as PDF"]}),(0,t.jsxs)("button",{className:"flex items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors",onClick:()=>{(e=>{let t=e.entries.filter(e=>null!==e.result),s=[["LLM Multi-Model Cost Estimate Report"],["Generated",new Date().toLocaleString()],[""]];for(let r of(s.push(["COMBINED TOTALS"],["Total Per Request",e.totals.cost_per_request.toString()],["Total Daily",e.totals.daily_cost?.toString()||"-"],["Total Monthly",e.totals.monthly_cost?.toString()||"-"],["Margin Per Request",e.totals.margin_per_request.toString()],["Daily Margin",e.totals.daily_margin?.toString()||"-"],["Monthly Margin",e.totals.monthly_margin?.toString()||"-"],[""]),s.push(["Model","Provider","Input Tokens","Output Tokens","Requests/Day","Requests/Month","Cost/Request","Daily Cost","Monthly Cost","Input Cost/Req","Output Cost/Req","Margin/Req"]),t)){let e=r.result;s.push([e.model,e.provider||"-",e.input_tokens.toString(),e.output_tokens.toString(),e.num_requests_per_day?.toString()||"-",e.num_requests_per_month?.toString()||"-",e.cost_per_request.toString(),e.daily_cost?.toString()||"-",e.monthly_cost?.toString()||"-",e.input_cost_per_request.toString(),e.output_cost_per_request.toString(),e.margin_cost_per_request.toString()])}let r=new Blob([s.map(e=>e.map(e=>`"${e}"`).join(",")).join("\n")],{type:"text/csv;charset=utf-8;"}),a=window.URL.createObjectURL(r),l=document.createElement("a");l.href=a,l.download=`cost_estimate_multi_model_${new Date().toISOString().split("T")[0]}.csv`,document.body.appendChild(l),l.click(),document.body.removeChild(l),window.URL.revokeObjectURL(a)})(e),a(!1)},children:[(0,t.jsx)(eC,{className:"mr-3 text-green-600"}),"Export as CSV"]})]})]}):null},eP=e=>null==e?"-":0===e?"$0":e<1e-4?`$${e.toExponential(2)}`:e<1?`$${e.toFixed(4)}`:`$${(0,ej.formatNumberWithCommas)(e,2,!0)}`,eM=({result:e,loading:s,timePeriod:r})=>{let l="day"===r?"Daily":"Monthly",n="day"===r?e.daily_cost:e.monthly_cost,o="day"===r?e.daily_input_cost:e.monthly_input_cost,i="day"===r?e.daily_output_cost:e.monthly_output_cost,d="day"===r?e.daily_margin_cost:e.monthly_margin_cost,c="day"===r?e.num_requests_per_day:e.num_requests_per_month;return(0,t.jsxs)("div",{className:"space-y-3 bg-gray-50 p-4 rounded-lg",children:[s&&(0,t.jsxs)("div",{className:"flex items-center gap-2 text-gray-500 text-sm",children:[(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"}),(0,t.jsx)("span",{children:"Updating..."})]}),(0,t.jsxs)("div",{className:"grid grid-cols-4 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Total/Request"}),(0,t.jsx)(a.Text,{className:"text-base font-semibold text-blue-600",children:eP(e.cost_per_request)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Input Cost"}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(e.input_cost_per_request)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Output Cost"}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(e.output_cost_per_request)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Margin Fee"}),(0,t.jsx)(a.Text,{className:`text-sm ${e.margin_cost_per_request>0?"text-amber-600":""}`,children:eP(e.margin_cost_per_request)})]})]}),null!==n&&(0,t.jsxs)("div",{className:"grid grid-cols-4 gap-4 pt-2 border-t border-gray-200",children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Total (",null==c?"-":(0,ej.formatNumberWithCommas)(c,0,!0)," req)"]}),(0,t.jsx)(a.Text,{className:`text-base font-semibold ${"day"===r?"text-green-600":"text-purple-600"}`,children:eP(n)})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Input"]}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(o)})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Output"]}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(i)})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Margin Fee"]}),(0,t.jsx)(a.Text,{className:`text-sm ${(d??0)>0?"text-amber-600":""}`,children:eP(d)})]})]}),(e.input_cost_per_token||e.output_cost_per_token)&&(0,t.jsxs)("div",{className:"text-xs text-gray-400 pt-2 border-t border-gray-200",children:["Token Pricing:"," ",e.input_cost_per_token&&(0,t.jsxs)("span",{children:["Input $",(0,ej.formatNumberWithCommas)(1e6*e.input_cost_per_token,2),"/1M"]}),e.input_cost_per_token&&e.output_cost_per_token&&" | ",e.output_cost_per_token&&(0,t.jsxs)("span",{children:["Output $",(0,ej.formatNumberWithCommas)(1e6*e.output_cost_per_token,2),"/1M"]})]})]})},eq=({multiResult:e,timePeriod:r})=>{let[n,o]=(0,s.useState)(new Set),i=e.entries.filter(e=>null!==e.result),d=e.entries.filter(e=>e.loading),c=e.entries.filter(e=>null!==e.error),m=i.length>0,u=d.length>0,x=c.length>0;if(!m&&!u&&!x)return(0,t.jsx)("div",{className:"py-6 text-center border border-dashed border-gray-300 rounded-lg bg-gray-50",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"Select models above to see cost estimates"})});if(!m&&u&&!x)return(0,t.jsxs)("div",{className:"py-6 text-center",children:[(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0})}),(0,t.jsx)(a.Text,{className:"text-gray-500 block mt-2",children:"Calculating costs..."})]});if(!m&&x)return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(ep.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)(a.Text,{className:"text-base font-semibold text-gray-900",children:"Cost Estimates"}),u&&(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"})]}),c.map(e=>(0,t.jsxs)("div",{className:"text-sm text-red-600 bg-red-50 p-3 rounded-lg border border-red-200",children:[(0,t.jsxs)("span",{className:"font-medium",children:[e.entry.model||"Unknown model",": "]}),e.error]},e.entry.id))]});let p=e.totals.margin_per_request>0,g="day"===r?"Daily":"Monthly",h=[{title:"Model",dataIndex:"model",key:"model",render:(e,s)=>(0,t.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"font-medium text-sm",children:e}),s.provider&&(0,t.jsx)(eh.Tag,{color:"blue",className:"text-xs",children:s.provider}),s.loading&&(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"})]}),s.error&&(0,t.jsxs)("div",{className:"text-xs text-red-600 bg-red-50 px-2 py-1 rounded-sm",children:["⚠️ ",s.error]}),s.hasZeroCost&&!s.error&&(0,t.jsx)("div",{className:"text-xs text-amber-600 bg-amber-50 px-2 py-1 rounded-sm",children:"⚠️ No pricing data found for this model. Set base_model in config."})]})},{title:"Per Request",dataIndex:"cost_per_request",key:"cost_per_request",align:"right",render:(e,s)=>s.error?(0,t.jsx)("span",{className:"text-gray-400",children:"-"}):(0,t.jsx)("span",{className:"font-mono text-sm",children:eP(e)})},{title:"Margin Fee",dataIndex:"margin_cost_per_request",key:"margin_cost_per_request",align:"right",render:(e,s)=>s.error?(0,t.jsx)("span",{className:"text-gray-400",children:"-"}):(0,t.jsx)("span",{className:`font-mono text-sm ${(e??0)>0?"text-amber-600":"text-gray-400"}`,children:eP(e)})},{title:g,dataIndex:"day"===r?"daily_cost":"monthly_cost",key:"period_cost",align:"right",render:(e,s)=>s.error?(0,t.jsx)("span",{className:"text-gray-400",children:"-"}):(0,t.jsx)("span",{className:"font-mono text-sm",children:eP(e)})},{title:"",key:"expand",width:40,render:(e,s)=>s.error?null:(0,t.jsx)(l.Button,{size:"xs",variant:"light",onClick:()=>{var e;return e=s.id,void o(t=>{let s=new Set(t);return s.has(e)?s.delete(e):s.add(e),s})},className:"text-gray-400 hover:text-gray-600",children:n.has(s.id)?(0,t.jsx)(ey.DownOutlined,{}):(0,t.jsx)(ev.RightOutlined,{})})}],f=e.entries.filter(e=>e.entry.model).map(e=>({key:e.entry.id,id:e.entry.id,model:e.result?.model||e.entry.model,provider:e.result?.provider,cost_per_request:e.result?.cost_per_request??null,margin_cost_per_request:e.result?.margin_cost_per_request??null,daily_cost:e.result?.daily_cost??null,monthly_cost:e.result?.monthly_cost??null,error:e.error,loading:e.loading,hasZeroCost:e.result&&0===e.result.cost_per_request}));return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(ep.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)(a.Text,{className:"text-base font-semibold text-gray-900",children:"Cost Estimates"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[u&&(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"}),(0,t.jsx)(e$,{multiResult:e})]})]}),(0,t.jsxs)(W.Card,{size:"small",className:"bg-linear-to-r from-slate-50 to-blue-50 border-slate-200",children:[(0,t.jsxs)(eu.Row,{gutter:[16,8],children:[(0,t.jsx)(ex.Col,{xs:24,sm:12,children:(0,t.jsx)(eo,{title:(0,t.jsx)("span",{className:"text-xs",children:"Total Per Request"}),value:eP(e.totals.cost_per_request),valueStyle:{color:"#1890ff",fontSize:"18px",fontFamily:"monospace"}})}),(0,t.jsx)(ex.Col,{xs:24,sm:12,children:(0,t.jsx)(eo,{title:(0,t.jsxs)("span",{className:"text-xs",children:["Total ",g]}),value:eP("day"===r?e.totals.daily_cost:e.totals.monthly_cost),valueStyle:{color:"day"===r?"#52c41a":"#722ed1",fontSize:"18px",fontFamily:"monospace"}})})]}),p&&(0,t.jsxs)(eu.Row,{gutter:[16,8],className:"mt-3 pt-3 border-t border-slate-200",children:[(0,t.jsxs)(ex.Col,{xs:24,sm:12,children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Margin Fee/Request"}),(0,t.jsx)("div",{className:"text-sm font-mono text-amber-600",children:eP(e.totals.margin_per_request)})]}),(0,t.jsxs)(ex.Col,{xs:24,sm:12,children:[(0,t.jsxs)("div",{className:"text-xs text-gray-500",children:[g," Margin Fee"]}),(0,t.jsx)("div",{className:"text-sm font-mono text-amber-600",children:eP("day"===r?e.totals.daily_margin:e.totals.monthly_margin)})]})]})]}),f.length>0&&(0,t.jsx)(z.Table,{columns:h,dataSource:f,pagination:!1,size:"small",className:"border border-gray-200 rounded-lg",expandable:{expandedRowKeys:Array.from(n),expandedRowRender:e=>{let s=i.find(t=>t.entry.id===e.id);return s?.result?(0,t.jsx)("div",{className:"py-2",children:(0,t.jsx)(eM,{result:s.result,loading:s.loading,timePeriod:r})}):null},showExpandColumn:!1}})]})};var eO=e.i(602869);let eE=()=>({id:`entry-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,model:"",input_tokens:1e3,output_tokens:500,num_requests_per_day:void 0,num_requests_per_month:void 0}),eF=({accessToken:e,models:r})=>{let[a,l]=(0,s.useState)([eE()]),[n,o]=(0,s.useState)("month"),{debouncedFetchForEntry:i,removeEntry:d,getMultiModelResult:c}=function(e){let[t,r]=(0,s.useState)(new Map),a=(0,s.useRef)(new Map),l=(0,s.useCallback)(async t=>{if(!e||!t.model)return void r(e=>{let s=new Map(e);return s.set(t.id,{entry:t,result:null,loading:!1,error:null}),s});r(e=>{let s=new Map(e),r=s.get(t.id);return s.set(t.id,{entry:t,result:r?.result??null,loading:!0,error:null}),s});try{let s=(0,eO.getProxyBaseUrl)(),a=s?`${s}/cost/estimate`:"/cost/estimate",l={model:t.model,input_tokens:t.input_tokens||0,output_tokens:t.output_tokens||0,num_requests_per_day:t.num_requests_per_day||null,num_requests_per_month:t.num_requests_per_month||null},n=await fetch(a,{method:"POST",headers:{[(0,eO.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(n.ok){let e=await n.json();r(s=>{let r=new Map(s);return r.set(t.id,{entry:t,result:e,loading:!1,error:null}),r})}else{let e=await n.json(),s=e.detail?.error||e.detail||"Failed to estimate cost";r(e=>{let r=new Map(e);return r.set(t.id,{entry:t,result:null,loading:!1,error:s}),r})}}catch(e){console.error("Error estimating cost:",e),r(e=>{let s=new Map(e);return s.set(t.id,{entry:t,result:null,loading:!1,error:"Network error"}),s})}},[e]),n=(0,s.useCallback)(e=>{let t=a.current.get(e.id);t&&clearTimeout(t);let s=setTimeout(()=>{l(e)},500);a.current.set(e.id,s)},[l]),o=(0,s.useCallback)(e=>{let t=a.current.get(e);t&&(clearTimeout(t),a.current.delete(e)),r(t=>{let s=new Map(t);return s.delete(e),s})},[]);return(0,s.useEffect)(()=>{let e=a.current;return()=>{e.forEach(e=>clearTimeout(e)),e.clear()}},[]),{debouncedFetchForEntry:n,removeEntry:o,getMultiModelResult:(0,s.useCallback)(e=>{let s=e.map(e=>{let s=t.get(e.id);return{entry:e,result:s?.result??null,loading:s?.loading??!1,error:s?.error??null}}),r=0,a=null,l=null,n=0,o=null,i=null;for(let e of s)e.result&&(r+=e.result.cost_per_request,n+=e.result.margin_cost_per_request,null!==e.result.daily_cost&&(a=(a??0)+e.result.daily_cost),null!==e.result.daily_margin_cost&&(o=(o??0)+e.result.daily_margin_cost),null!==e.result.monthly_cost&&(l=(l??0)+e.result.monthly_cost),null!==e.result.monthly_margin_cost&&(i=(i??0)+e.result.monthly_margin_cost));return{entries:s,totals:{cost_per_request:r,daily_cost:a,monthly_cost:l,margin_per_request:n,daily_margin:o,monthly_margin:i}}},[t])}}(e),m=(0,s.useCallback)((e,t,s)=>{l(r=>{let a=r.map(r=>r.id===e?{...r,[t]:s}:r),l=a.find(t=>t.id===e);return l&&l.model&&i(l),a})},[i]),u=(0,s.useCallback)(e=>{o(e),l(t=>t.map(t=>({...t,num_requests_per_day:"day"===e?t.num_requests_per_day:void 0,num_requests_per_month:"month"===e?t.num_requests_per_month:void 0})))},[]),x=(0,s.useCallback)(()=>{l(e=>[...e,eE()])},[]),p=(0,s.useCallback)(e=>{l(t=>t.filter(t=>t.id!==e)),d(e)},[d]),g=c(a),h=[{title:"Model",dataIndex:"model",key:"model",width:"35%",render:(e,s)=>(0,t.jsx)(F.Select,{showSearch:!0,placeholder:"Select a model",value:s.model||void 0,onChange:e=>m(s.id,"model",e),optionFilterProp:"label",filterOption:(e,t)=>String(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:r.map(e=>({value:e,label:e})),style:{width:"100%"},size:"small"})},{title:"Input Tokens",dataIndex:"input_tokens",key:"input_tokens",width:"18%",render:(e,s)=>(0,t.jsx)(H.InputNumber,{min:0,value:s.input_tokens,onChange:e=>m(s.id,"input_tokens",e??0),style:{width:"100%"},size:"small",formatter:e=>`${e}`.replace(/\B(?=(\d{3})+(?!\d))/g,",")})},{title:"Output Tokens",dataIndex:"output_tokens",key:"output_tokens",width:"18%",render:(e,s)=>(0,t.jsx)(H.InputNumber,{min:0,value:s.output_tokens,onChange:e=>m(s.id,"output_tokens",e??0),style:{width:"100%"},size:"small",formatter:e=>`${e}`.replace(/\B(?=(\d{3})+(?!\d))/g,",")})},{title:`Requests/${"day"===n?"Day":"Month"}`,dataIndex:"day"===n?"num_requests_per_day":"num_requests_per_month",key:"num_requests",width:"20%",render:(e,s)=>(0,t.jsx)(H.InputNumber,{min:0,value:"day"===n?s.num_requests_per_day:s.num_requests_per_month,onChange:e=>m(s.id,"day"===n?"num_requests_per_day":"num_requests_per_month",e??void 0),style:{width:"100%"},size:"small",placeholder:"-",formatter:e=>e?`${e}`.replace(/\B(?=(\d{3})+(?!\d))/g,","):""})},{title:"",key:"actions",width:50,render:(e,s)=>(0,t.jsx)(G.Button,{type:"text",icon:(0,t.jsx)(V.DeleteOutlined,{}),onClick:()=>p(s.id),disabled:1===a.length,danger:!0,size:"small"})}];return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"flex items-center justify-end mb-2",children:(0,t.jsxs)(A.Radio.Group,{value:n,onChange:e=>u(e.target.value),size:"small",optionType:"button",buttonStyle:"solid",children:[(0,t.jsx)(A.Radio.Button,{value:"day",children:"Per Day"}),(0,t.jsx)(A.Radio.Button,{value:"month",children:"Per Month"})]})}),(0,t.jsx)(z.Table,{columns:h,dataSource:a,rowKey:"id",pagination:!1,size:"small",footer:()=>(0,t.jsx)(G.Button,{type:"dashed",onClick:x,icon:(0,t.jsx)(U.PlusOutlined,{}),className:"w-full",children:"Add Another Model"})}),(0,t.jsx)(eq,{multiResult:g,timePeriod:n})]})};var eR=e.i(270377),eL=e.i(778917),eI=e.i(664659);let eD=({items:e,children:r="Docs",className:a=""})=>{let[l,n]=(0,s.useState)(!1),o=(0,s.useRef)(null);return(0,s.useEffect)(()=>{let e=e=>{o.current&&!o.current.contains(e.target)&&n(!1)};return l&&document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[l]),(0,t.jsxs)("div",{className:`relative inline-block ${a}`,ref:o,children:[(0,t.jsxs)("button",{type:"button",onClick:()=>n(!l),className:"inline-flex items-center gap-1 text-gray-500 hover:text-gray-700 text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 rounded-sm px-2 py-1","aria-expanded":l,"aria-haspopup":"true",children:[(0,t.jsx)("span",{children:r}),(0,t.jsx)(eI.ChevronDown,{className:`h-3 w-3 transition-transform ${l?"rotate-180":""}`,"aria-hidden":"true"})]}),l&&(0,t.jsx)("div",{className:"absolute right-0 mt-1 w-56 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50",children:e.map((e,s)=>(0,t.jsxs)("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",className:"flex items-center justify-between px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors",onClick:()=>n(!1),children:[(0,t.jsx)("span",{children:e.label}),(0,t.jsx)(eL.ExternalLink,{className:"h-3.5 w-3.5 text-gray-400 shrink-0 ml-2","aria-hidden":"true"})]},s))})]})};var eA=e.i(673709);let eB=()=>{let[e,r]=(0,s.useState)(""),[l,n]=(0,s.useState)(""),o=(0,s.useMemo)(()=>{let t=parseFloat(e),s=parseFloat(l);if(isNaN(t)||isNaN(s)||0===t||0===s)return null;let r=t+s,a=s/r*100;return{originalCost:r.toFixed(10),finalCost:t.toFixed(10),discountAmount:s.toFixed(10),discountPercentage:a.toFixed(2)}},[e,l]);return(0,t.jsxs)("div",{className:"space-y-4 pt-2",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-1",children:"Cost Calculation"}),(0,t.jsxs)(a.Text,{className:"text-xs text-gray-600",children:["Discounts are applied to provider costs:"," ",(0,t.jsx)("code",{className:"bg-gray-100 px-1.5 py-0.5 rounded-sm text-xs",children:"final_cost = base_cost × (1 - discount%/100)"})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-1",children:"Example"}),(0,t.jsx)(a.Text,{className:"text-xs text-gray-600",children:"A 5% discount on a $10.00 request results in: $10.00 × (1 - 0.05) = $9.50"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-1",children:"Valid Range"}),(0,t.jsx)(a.Text,{className:"text-xs text-gray-600",children:"Discount percentages must be between 0% and 100%"})]}),(0,t.jsxs)("div",{className:"pt-4 border-t border-gray-200",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-2",children:"Validating Discounts"}),(0,t.jsx)(a.Text,{className:"text-xs text-gray-600 mb-3",children:"Make a test request and check the response headers to verify discounts are applied:"}),(0,t.jsx)(eA.default,{language:"bash",code:`curl -X POST -i http://your-proxy:4000/chat/completions \\ + `;t.document.write(a),t.document.close(),t.onload=()=>{t.print()}})(e),a(!1)},children:[(0,t.jsx)(e_.FilePdfOutlined,{className:"mr-3 text-red-500"}),"Export as PDF"]}),(0,t.jsxs)("button",{className:"flex items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors",onClick:()=>{(e=>{let t=e.entries.filter(e=>null!==e.result),s=[["LLM Multi-Model Cost Estimate Report"],["Generated",new Date().toLocaleString()],[""]];for(let r of(s.push(["COMBINED TOTALS"],["Total Per Request",e.totals.cost_per_request.toString()],["Total Daily",e.totals.daily_cost?.toString()||"-"],["Total Monthly",e.totals.monthly_cost?.toString()||"-"],["Margin Per Request",e.totals.margin_per_request.toString()],["Daily Margin",e.totals.daily_margin?.toString()||"-"],["Monthly Margin",e.totals.monthly_margin?.toString()||"-"],[""]),s.push(["Model","Provider","Input Tokens","Output Tokens","Requests/Day","Requests/Month","Cost/Request","Daily Cost","Monthly Cost","Input Cost/Req","Output Cost/Req","Margin/Req"]),t)){let e=r.result;s.push([e.model,e.provider||"-",e.input_tokens.toString(),e.output_tokens.toString(),e.num_requests_per_day?.toString()||"-",e.num_requests_per_month?.toString()||"-",e.cost_per_request.toString(),e.daily_cost?.toString()||"-",e.monthly_cost?.toString()||"-",e.input_cost_per_request.toString(),e.output_cost_per_request.toString(),e.margin_cost_per_request.toString()])}let r=new Blob([s.map(e=>e.map(e=>`"${e}"`).join(",")).join("\n")],{type:"text/csv;charset=utf-8;"}),a=window.URL.createObjectURL(r),l=document.createElement("a");l.href=a,l.download=`cost_estimate_multi_model_${new Date().toISOString().split("T")[0]}.csv`,document.body.appendChild(l),l.click(),document.body.removeChild(l),window.URL.revokeObjectURL(a)})(e),a(!1)},children:[(0,t.jsx)(eC,{className:"mr-3 text-green-600"}),"Export as CSV"]})]})]}):null},eP=e=>null==e?"-":0===e?"$0":e<1e-4?`$${e.toExponential(2)}`:e<1?`$${e.toFixed(4)}`:`$${(0,ej.formatNumberWithCommas)(e,2,!0)}`,eM=({result:e,loading:s,timePeriod:r})=>{let l="day"===r?"Daily":"Monthly",n="day"===r?e.daily_cost:e.monthly_cost,o="day"===r?e.daily_input_cost:e.monthly_input_cost,i="day"===r?e.daily_output_cost:e.monthly_output_cost,d="day"===r?e.daily_margin_cost:e.monthly_margin_cost,c="day"===r?e.num_requests_per_day:e.num_requests_per_month;return(0,t.jsxs)("div",{className:"space-y-3 bg-gray-50 p-4 rounded-lg",children:[s&&(0,t.jsxs)("div",{className:"flex items-center gap-2 text-gray-500 text-sm",children:[(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"}),(0,t.jsx)("span",{children:"Updating..."})]}),(0,t.jsxs)("div",{className:"grid grid-cols-4 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Total/Request"}),(0,t.jsx)(a.Text,{className:"text-base font-semibold text-blue-600",children:eP(e.cost_per_request)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Input Cost"}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(e.input_cost_per_request)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Output Cost"}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(e.output_cost_per_request)})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 block",children:"Margin Fee"}),(0,t.jsx)(a.Text,{className:`text-sm ${e.margin_cost_per_request>0?"text-amber-600":""}`,children:eP(e.margin_cost_per_request)})]})]}),null!==n&&(0,t.jsxs)("div",{className:"grid grid-cols-4 gap-4 pt-2 border-t border-gray-200",children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Total (",null==c?"-":(0,ej.formatNumberWithCommas)(c,0,!0)," req)"]}),(0,t.jsx)(a.Text,{className:`text-base font-semibold ${"day"===r?"text-green-600":"text-purple-600"}`,children:eP(n)})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Input"]}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(o)})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Output"]}),(0,t.jsx)(a.Text,{className:"text-sm",children:eP(i)})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(a.Text,{className:"text-xs text-gray-500 block",children:[l," Margin Fee"]}),(0,t.jsx)(a.Text,{className:`text-sm ${(d??0)>0?"text-amber-600":""}`,children:eP(d)})]})]}),(e.input_cost_per_token||e.output_cost_per_token)&&(0,t.jsxs)("div",{className:"text-xs text-gray-400 pt-2 border-t border-gray-200",children:["Token Pricing:"," ",e.input_cost_per_token&&(0,t.jsxs)("span",{children:["Input $",(0,ej.formatNumberWithCommas)(1e6*e.input_cost_per_token,2),"/1M"]}),e.input_cost_per_token&&e.output_cost_per_token&&" | ",e.output_cost_per_token&&(0,t.jsxs)("span",{children:["Output $",(0,ej.formatNumberWithCommas)(1e6*e.output_cost_per_token,2),"/1M"]})]})]})},eq=({multiResult:e,timePeriod:r})=>{let[n,o]=(0,s.useState)(new Set),i=e.entries.filter(e=>null!==e.result),d=e.entries.filter(e=>e.loading),c=e.entries.filter(e=>null!==e.error),m=i.length>0,u=d.length>0,x=c.length>0;if(!m&&!u&&!x)return(0,t.jsx)("div",{className:"py-6 text-center border border-dashed border-gray-300 rounded-lg bg-gray-50",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"Select models above to see cost estimates"})});if(!m&&u&&!x)return(0,t.jsxs)("div",{className:"py-6 text-center",children:[(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0})}),(0,t.jsx)(a.Text,{className:"text-gray-500 block mt-2",children:"Calculating costs..."})]});if(!m&&x)return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(ep.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)(a.Text,{className:"text-base font-semibold text-gray-900",children:"Cost Estimates"}),u&&(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"})]}),c.map(e=>(0,t.jsxs)("div",{className:"text-sm text-red-600 bg-red-50 p-3 rounded-lg border border-red-200",children:[(0,t.jsxs)("span",{className:"font-medium",children:[e.entry.model||"Unknown model",": "]}),e.error]},e.entry.id))]});let p=e.totals.margin_per_request>0,g="day"===r?"Daily":"Monthly",h=[{title:"Model",dataIndex:"model",key:"model",render:(e,s)=>(0,t.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"font-medium text-sm",children:e}),s.provider&&(0,t.jsx)(eh.Tag,{color:"blue",className:"text-xs",children:s.provider}),s.loading&&(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"})]}),s.error&&(0,t.jsxs)("div",{className:"text-xs text-red-600 bg-red-50 px-2 py-1 rounded-sm",children:["⚠️ ",s.error]}),s.hasZeroCost&&!s.error&&(0,t.jsx)("div",{className:"text-xs text-amber-600 bg-amber-50 px-2 py-1 rounded-sm",children:"⚠️ No pricing data found for this model. Set base_model in config."})]})},{title:"Per Request",dataIndex:"cost_per_request",key:"cost_per_request",align:"right",render:(e,s)=>s.error?(0,t.jsx)("span",{className:"text-gray-400",children:"-"}):(0,t.jsx)("span",{className:"font-mono text-sm",children:eP(e)})},{title:"Margin Fee",dataIndex:"margin_cost_per_request",key:"margin_cost_per_request",align:"right",render:(e,s)=>s.error?(0,t.jsx)("span",{className:"text-gray-400",children:"-"}):(0,t.jsx)("span",{className:`font-mono text-sm ${(e??0)>0?"text-amber-600":"text-gray-400"}`,children:eP(e)})},{title:g,dataIndex:"day"===r?"daily_cost":"monthly_cost",key:"period_cost",align:"right",render:(e,s)=>s.error?(0,t.jsx)("span",{className:"text-gray-400",children:"-"}):(0,t.jsx)("span",{className:"font-mono text-sm",children:eP(e)})},{title:"",key:"expand",width:40,render:(e,s)=>s.error?null:(0,t.jsx)(l.Button,{size:"xs",variant:"light",onClick:()=>{var e;return e=s.id,void o(t=>{let s=new Set(t);return s.has(e)?s.delete(e):s.add(e),s})},className:"text-gray-400 hover:text-gray-600",children:n.has(s.id)?(0,t.jsx)(ey.DownOutlined,{}):(0,t.jsx)(ev.RightOutlined,{})})}],f=e.entries.filter(e=>e.entry.model).map(e=>({key:e.entry.id,id:e.entry.id,model:e.result?.model||e.entry.model,provider:e.result?.provider,cost_per_request:e.result?.cost_per_request??null,margin_cost_per_request:e.result?.margin_cost_per_request??null,daily_cost:e.result?.daily_cost??null,monthly_cost:e.result?.monthly_cost??null,error:e.error,loading:e.loading,hasZeroCost:e.result&&0===e.result.cost_per_request}));return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(ep.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)(a.Text,{className:"text-base font-semibold text-gray-900",children:"Cost Estimates"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[u&&(0,t.jsx)(eg.Spin,{indicator:(0,t.jsx)(ef.LoadingOutlined,{spin:!0}),size:"small"}),(0,t.jsx)(e$,{multiResult:e})]})]}),(0,t.jsxs)(W.Card,{size:"small",className:"bg-linear-to-r from-slate-50 to-blue-50 border-slate-200",children:[(0,t.jsxs)(eu.Row,{gutter:[16,8],children:[(0,t.jsx)(ex.Col,{xs:24,sm:12,children:(0,t.jsx)(eo,{title:(0,t.jsx)("span",{className:"text-xs",children:"Total Per Request"}),value:eP(e.totals.cost_per_request),valueStyle:{color:"#1890ff",fontSize:"18px",fontFamily:"monospace"}})}),(0,t.jsx)(ex.Col,{xs:24,sm:12,children:(0,t.jsx)(eo,{title:(0,t.jsxs)("span",{className:"text-xs",children:["Total ",g]}),value:eP("day"===r?e.totals.daily_cost:e.totals.monthly_cost),valueStyle:{color:"day"===r?"#52c41a":"#722ed1",fontSize:"18px",fontFamily:"monospace"}})})]}),p&&(0,t.jsxs)(eu.Row,{gutter:[16,8],className:"mt-3 pt-3 border-t border-slate-200",children:[(0,t.jsxs)(ex.Col,{xs:24,sm:12,children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Margin Fee/Request"}),(0,t.jsx)("div",{className:"text-sm font-mono text-amber-600",children:eP(e.totals.margin_per_request)})]}),(0,t.jsxs)(ex.Col,{xs:24,sm:12,children:[(0,t.jsxs)("div",{className:"text-xs text-gray-500",children:[g," Margin Fee"]}),(0,t.jsx)("div",{className:"text-sm font-mono text-amber-600",children:eP("day"===r?e.totals.daily_margin:e.totals.monthly_margin)})]})]})]}),f.length>0&&(0,t.jsx)(z.Table,{columns:h,dataSource:f,pagination:!1,size:"small",className:"border border-gray-200 rounded-lg",expandable:{expandedRowKeys:Array.from(n),expandedRowRender:e=>{let s=i.find(t=>t.entry.id===e.id);return s?.result?(0,t.jsx)("div",{className:"py-2",children:(0,t.jsx)(eM,{result:s.result,loading:s.loading,timePeriod:r})}):null},showExpandColumn:!1}})]})};var eO=e.i(602869);let eE=()=>({id:`entry-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,model:"",input_tokens:1e3,output_tokens:500,num_requests_per_day:void 0,num_requests_per_month:void 0}),eF=({accessToken:e,models:r})=>{let[a,l]=(0,s.useState)([eE()]),[n,o]=(0,s.useState)("month"),{debouncedFetchForEntry:i,removeEntry:d,getMultiModelResult:c}=function(e){let[t,r]=(0,s.useState)(new Map),a=(0,s.useRef)(new Map),l=(0,s.useCallback)(async t=>{if(!e||!t.model)return void r(e=>{let s=new Map(e);return s.set(t.id,{entry:t,result:null,loading:!1,error:null}),s});r(e=>{let s=new Map(e),r=s.get(t.id);return s.set(t.id,{entry:t,result:r?.result??null,loading:!0,error:null}),s});try{let s=(0,eO.getProxyBaseUrl)(),a=s?`${s}/cost/estimate`:"/cost/estimate",l={model:t.model,input_tokens:t.input_tokens||0,output_tokens:t.output_tokens||0,num_requests_per_day:t.num_requests_per_day||null,num_requests_per_month:t.num_requests_per_month||null},n=await fetch(a,{method:"POST",headers:{[(0,eO.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(n.ok){let e=await n.json();r(s=>{let r=new Map(s);return r.set(t.id,{entry:t,result:e,loading:!1,error:null}),r})}else{let e=await n.json(),s=e.detail?.error||e.detail||"Failed to estimate cost";r(e=>{let r=new Map(e);return r.set(t.id,{entry:t,result:null,loading:!1,error:s}),r})}}catch(e){console.error("Error estimating cost:",e),r(e=>{let s=new Map(e);return s.set(t.id,{entry:t,result:null,loading:!1,error:"Network error"}),s})}},[e]),n=(0,s.useCallback)(e=>{let t=a.current.get(e.id);t&&clearTimeout(t);let s=setTimeout(()=>{l(e)},500);a.current.set(e.id,s)},[l]),o=(0,s.useCallback)(e=>{let t=a.current.get(e);t&&(clearTimeout(t),a.current.delete(e)),r(t=>{let s=new Map(t);return s.delete(e),s})},[]);return(0,s.useEffect)(()=>{let e=a.current;return()=>{e.forEach(e=>clearTimeout(e)),e.clear()}},[]),{debouncedFetchForEntry:n,removeEntry:o,getMultiModelResult:(0,s.useCallback)(e=>{let s=e.map(e=>{let s=t.get(e.id);return{entry:e,result:s?.result??null,loading:s?.loading??!1,error:s?.error??null}}),r=0,a=null,l=null,n=0,o=null,i=null;for(let e of s)e.result&&(r+=e.result.cost_per_request,n+=e.result.margin_cost_per_request,null!==e.result.daily_cost&&(a=(a??0)+e.result.daily_cost),null!==e.result.daily_margin_cost&&(o=(o??0)+e.result.daily_margin_cost),null!==e.result.monthly_cost&&(l=(l??0)+e.result.monthly_cost),null!==e.result.monthly_margin_cost&&(i=(i??0)+e.result.monthly_margin_cost));return{entries:s,totals:{cost_per_request:r,daily_cost:a,monthly_cost:l,margin_per_request:n,daily_margin:o,monthly_margin:i}}},[t])}}(e),m=(0,s.useCallback)((e,t,s)=>{l(r=>{let a=r.map(r=>r.id===e?{...r,[t]:s}:r),l=a.find(t=>t.id===e);return l&&l.model&&i(l),a})},[i]),u=(0,s.useCallback)(e=>{o(e),l(t=>t.map(t=>({...t,num_requests_per_day:"day"===e?t.num_requests_per_day:void 0,num_requests_per_month:"month"===e?t.num_requests_per_month:void 0})))},[]),x=(0,s.useCallback)(()=>{l(e=>[...e,eE()])},[]),p=(0,s.useCallback)(e=>{l(t=>t.filter(t=>t.id!==e)),d(e)},[d]),g=c(a),h=[{title:"Model",dataIndex:"model",key:"model",width:"35%",render:(e,s)=>(0,t.jsx)(F.Select,{showSearch:!0,placeholder:"Select a model",value:s.model||void 0,onChange:e=>m(s.id,"model",e),optionFilterProp:"label",filterOption:(e,t)=>String(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:r.map(e=>({value:e,label:e})),style:{width:"100%"},size:"small"})},{title:"Input Tokens",dataIndex:"input_tokens",key:"input_tokens",width:"18%",render:(e,s)=>(0,t.jsx)(H.InputNumber,{min:0,value:s.input_tokens,onChange:e=>m(s.id,"input_tokens",e??0),style:{width:"100%"},size:"small",formatter:e=>`${e}`.replace(/\B(?=(\d{3})+(?!\d))/g,",")})},{title:"Output Tokens",dataIndex:"output_tokens",key:"output_tokens",width:"18%",render:(e,s)=>(0,t.jsx)(H.InputNumber,{min:0,value:s.output_tokens,onChange:e=>m(s.id,"output_tokens",e??0),style:{width:"100%"},size:"small",formatter:e=>`${e}`.replace(/\B(?=(\d{3})+(?!\d))/g,",")})},{title:`Requests/${"day"===n?"Day":"Month"}`,dataIndex:"day"===n?"num_requests_per_day":"num_requests_per_month",key:"num_requests",width:"20%",render:(e,s)=>(0,t.jsx)(H.InputNumber,{min:0,value:"day"===n?s.num_requests_per_day:s.num_requests_per_month,onChange:e=>m(s.id,"day"===n?"num_requests_per_day":"num_requests_per_month",e??void 0),style:{width:"100%"},size:"small",placeholder:"-",formatter:e=>e?`${e}`.replace(/\B(?=(\d{3})+(?!\d))/g,","):""})},{title:"",key:"actions",width:50,render:(e,s)=>(0,t.jsx)(G.Button,{type:"text",icon:(0,t.jsx)(V.DeleteOutlined,{}),onClick:()=>p(s.id),disabled:1===a.length,danger:!0,size:"small"})}];return(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"flex items-center justify-end mb-2",children:(0,t.jsxs)(A.Radio.Group,{value:n,onChange:e=>u(e.target.value),size:"small",optionType:"button",buttonStyle:"solid",children:[(0,t.jsx)(A.Radio.Button,{value:"day",children:"Per Day"}),(0,t.jsx)(A.Radio.Button,{value:"month",children:"Per Month"})]})}),(0,t.jsx)(z.Table,{columns:h,dataSource:a,rowKey:"id",pagination:!1,size:"small",footer:()=>(0,t.jsx)(G.Button,{type:"dashed",onClick:x,icon:(0,t.jsx)(U.PlusOutlined,{}),className:"w-full",children:"Add Another Model"})}),(0,t.jsx)(eq,{multiResult:g,timePeriod:n})]})};var eR=e.i(270377),eL=e.i(778917),eI=e.i(664659);let eD=({items:e,children:r="Docs",className:a=""})=>{let[l,n]=(0,s.useState)(!1),o=(0,s.useRef)(null);return(0,s.useEffect)(()=>{let e=e=>{o.current&&!o.current.contains(e.target)&&n(!1)};return l&&document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[l]),(0,t.jsxs)("div",{className:`relative inline-block ${a}`,ref:o,children:[(0,t.jsxs)("button",{type:"button",onClick:()=>n(!l),className:"inline-flex items-center gap-1 text-gray-500 hover:text-gray-700 text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 rounded-sm px-2 py-1","aria-expanded":l,"aria-haspopup":"true",children:[(0,t.jsx)("span",{children:r}),(0,t.jsx)(eI.ChevronDown,{className:`h-3 w-3 transition-transform ${l?"rotate-180":""}`,"aria-hidden":"true"})]}),l&&(0,t.jsx)("div",{className:"absolute right-0 mt-1 w-56 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50",children:e.map((e,s)=>(0,t.jsxs)("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",className:"flex items-center justify-between px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors",onClick:()=>n(!1),children:[(0,t.jsx)("span",{children:e.label}),(0,t.jsx)(eL.ExternalLink,{className:"h-3.5 w-3.5 text-gray-400 shrink-0 ml-2","aria-hidden":"true"})]},s))})]})};var eA=e.i(466828);let eB=()=>{let[e,r]=(0,s.useState)(""),[l,n]=(0,s.useState)(""),o=(0,s.useMemo)(()=>{let t=parseFloat(e),s=parseFloat(l);if(isNaN(t)||isNaN(s)||0===t||0===s)return null;let r=t+s,a=s/r*100;return{originalCost:r.toFixed(10),finalCost:t.toFixed(10),discountAmount:s.toFixed(10),discountPercentage:a.toFixed(2)}},[e,l]);return(0,t.jsxs)("div",{className:"space-y-4 pt-2",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-1",children:"Cost Calculation"}),(0,t.jsxs)(a.Text,{className:"text-xs text-gray-600",children:["Discounts are applied to provider costs:"," ",(0,t.jsx)("code",{className:"bg-gray-100 px-1.5 py-0.5 rounded-sm text-xs",children:"final_cost = base_cost × (1 - discount%/100)"})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-1",children:"Example"}),(0,t.jsx)(a.Text,{className:"text-xs text-gray-600",children:"A 5% discount on a $10.00 request results in: $10.00 × (1 - 0.05) = $9.50"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-1",children:"Valid Range"}),(0,t.jsx)(a.Text,{className:"text-xs text-gray-600",children:"Discount percentages must be between 0% and 100%"})]}),(0,t.jsxs)("div",{className:"pt-4 border-t border-gray-200",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm mb-2",children:"Validating Discounts"}),(0,t.jsx)(a.Text,{className:"text-xs text-gray-600 mb-3",children:"Make a test request and check the response headers to verify discounts are applied:"}),(0,t.jsx)(eA.default,{language:"bash",code:`curl -X POST -i http://your-proxy:4000/chat/completions \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer sk-1234" \\ -d '{ diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0pu3ltw1cci2~.js b/litellm/proxy/_experimental/out/_next/static/chunks/068p6o.s_qzmk.js similarity index 55% rename from litellm/proxy/_experimental/out/_next/static/chunks/0pu3ltw1cci2~.js rename to litellm/proxy/_experimental/out/_next/static/chunks/068p6o.s_qzmk.js index 481b9e60300..3af24ecefd2 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/0pu3ltw1cci2~.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/068p6o.s_qzmk.js @@ -1,4 +1,4 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),s=e.i(444755),o=e.i(673706),i=e.i(271645);let l=i.default.forwardRef((e,l)=>{let{color:a,children:n,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return i.default.createElement("p",Object.assign({ref:l,className:(0,s.tremorTwMerge)("font-medium text-tremor-title",a?(0,o.getColorClassNames)(a,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),n)});l.displayName="Title",e.s(["Title",0,l],629569)},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},411929,e=>{"use strict";var t=e.i(843476),r=e.i(271645),s=e.i(464571),o=e.i(166406),i=e.i(629569),l=e.i(602869),a=e.i(727749);let n=({accessToken:e})=>{let[n,d]=(0,r.useState)(`{ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),s=e.i(673706),l=e.i(271645);let a=l.default.forwardRef((e,a)=>{let{color:i,children:n,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return l.default.createElement("p",Object.assign({ref:a,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",i?(0,s.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),n)});a.displayName="Title",e.s(["Title",0,a],629569)},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},411929,e=>{"use strict";var t=e.i(843476),r=e.i(271645),o=e.i(464571),s=e.i(166406),l=e.i(629569),a=e.i(602869),i=e.i(727749);let n=({accessToken:e})=>{let[n,d]=(0,r.useState)(`{ "model": "openai/gpt-4o", "messages": [ { @@ -13,13 +13,13 @@ "temperature": 0.7, "max_tokens": 500, "stream": true -}`),[c,p]=(0,r.useState)(""),[u,m]=(0,r.useState)(!1),x=async()=>{m(!0);try{let o;try{o=JSON.parse(n)}catch(e){a.default.fromBackend("Invalid JSON in request body"),m(!1);return}let i={call_type:"completion",request_body:o};if(!e){a.default.fromBackend("No access token found"),m(!1);return}let d=await (0,l.transformRequestCall)(e,i);if(d.raw_request_api_base&&d.raw_request_body){var t,r,s;let e,o,i=(t=d.raw_request_api_base,r=d.raw_request_body,s=d.raw_request_headers||{},e=JSON.stringify(r,null,2).split("\n").map(e=>` ${e}`).join("\n"),o=Object.entries(s).map(([e,t])=>`-H '${e}: ${t}'`).join(" \\\n "),`curl -X POST \\ +}`),[c,u]=(0,r.useState)(""),[p,x]=(0,r.useState)(!1),m=async()=>{x(!0);try{let s;try{s=JSON.parse(n)}catch(e){i.default.fromBackend("Invalid JSON in request body"),x(!1);return}let l={call_type:"completion",request_body:s};if(!e){i.default.fromBackend("No access token found"),x(!1);return}let d=await (0,a.transformRequestCall)(e,l);if(d.raw_request_api_base&&d.raw_request_body){var t,r,o;let e,s,l=(t=d.raw_request_api_base,r=d.raw_request_body,o=d.raw_request_headers||{},e=JSON.stringify(r,null,2).split("\n").map(e=>` ${e}`).join("\n"),s=Object.entries(o).map(([e,t])=>`-H '${e}: ${t}'`).join(" \\\n "),`curl -X POST \\ ${t} \\ - ${o?`${o} \\ + ${s?`${s} \\ `:""}-H 'Content-Type: application/json' \\ -d '{ ${e} - }'`);p(i),a.default.success("Request transformed successfully")}else{let e="string"==typeof d?d:JSON.stringify(d);p(e),a.default.info("Transformed request received in unexpected format")}}catch(e){console.error("Error transforming request:",e),a.default.fromBackend("Failed to transform request")}finally{m(!1)}};return(0,t.jsxs)("div",{className:"w-full m-2",style:{overflow:"hidden"},children:[(0,t.jsx)(i.Title,{children:"Playground"}),(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"See how LiteLLM transforms your request for the specified provider."}),(0,t.jsxs)("div",{style:{display:"flex",gap:"16px",width:"100%",minWidth:0,overflow:"hidden"},className:"mt-4",children:[(0,t.jsxs)("div",{style:{flex:"1 1 50%",display:"flex",flexDirection:"column",border:"1px solid #e8e8e8",borderRadius:"8px",padding:"24px",overflow:"hidden",maxHeight:"600px",minWidth:0},children:[(0,t.jsxs)("div",{style:{marginBottom:"24px"},children:[(0,t.jsx)("h2",{style:{fontSize:"24px",fontWeight:"bold",margin:"0 0 4px 0"},children:"Original Request"}),(0,t.jsx)("p",{style:{color:"#666",margin:0},children:"The request you would send to LiteLLM /chat/completions endpoint."})]}),(0,t.jsx)("textarea",{style:{flex:"1 1 auto",width:"100%",minHeight:"240px",padding:"16px",border:"1px solid #e8e8e8",borderRadius:"6px",fontFamily:"monospace",fontSize:"14px",resize:"none",marginBottom:"24px",overflow:"auto"},value:n,onChange:e=>d(e.target.value),onKeyDown:e=>{(e.metaKey||e.ctrlKey)&&"Enter"===e.key&&(e.preventDefault(),x())},placeholder:"Press Cmd/Ctrl + Enter to transform"}),(0,t.jsx)("div",{style:{display:"flex",justifyContent:"flex-end",marginTop:"auto"},children:(0,t.jsxs)(s.Button,{type:"primary",style:{backgroundColor:"#000",display:"flex",alignItems:"center",gap:"8px"},onClick:x,loading:u,children:[(0,t.jsx)("span",{children:"Transform"}),(0,t.jsx)("span",{children:"→"})]})})]}),(0,t.jsxs)("div",{style:{flex:"1 1 50%",display:"flex",flexDirection:"column",border:"1px solid #e8e8e8",borderRadius:"8px",padding:"24px",overflow:"hidden",maxHeight:"800px",minWidth:0},children:[(0,t.jsxs)("div",{style:{marginBottom:"24px"},children:[(0,t.jsx)("h2",{style:{fontSize:"24px",fontWeight:"bold",margin:"0 0 4px 0"},children:"Transformed Request"}),(0,t.jsx)("p",{style:{color:"#666",margin:0},children:"How LiteLLM transforms your request for the specified provider."}),(0,t.jsx)("br",{}),(0,t.jsx)("p",{style:{color:"#666",margin:0},className:"text-xs",children:"Note: Sensitive headers are not shown."})]}),(0,t.jsxs)("div",{style:{position:"relative",backgroundColor:"#f5f5f5",borderRadius:"6px",flex:"1 1 auto",display:"flex",flexDirection:"column",overflow:"hidden"},children:[(0,t.jsx)("pre",{style:{padding:"16px",fontFamily:"monospace",fontSize:"14px",margin:0,overflow:"auto",flex:"1 1 auto"},children:c||`curl -X POST \\ + }'`);u(l),i.default.success("Request transformed successfully")}else{let e="string"==typeof d?d:JSON.stringify(d);u(e),i.default.info("Transformed request received in unexpected format")}}catch(e){console.error("Error transforming request:",e),i.default.fromBackend("Failed to transform request")}finally{x(!1)}};return(0,t.jsxs)("div",{className:"w-full m-2",style:{overflow:"hidden"},children:[(0,t.jsx)(l.Title,{children:"Playground"}),(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"See how LiteLLM transforms your request for the specified provider."}),(0,t.jsxs)("div",{style:{display:"flex",gap:"16px",width:"100%",minWidth:0,overflow:"hidden"},className:"mt-4",children:[(0,t.jsxs)("div",{style:{flex:"1 1 50%",display:"flex",flexDirection:"column",border:"1px solid #e8e8e8",borderRadius:"8px",padding:"24px",overflow:"hidden",maxHeight:"600px",minWidth:0},children:[(0,t.jsxs)("div",{style:{marginBottom:"24px"},children:[(0,t.jsx)("h2",{style:{fontSize:"24px",fontWeight:"bold",margin:"0 0 4px 0"},children:"Original Request"}),(0,t.jsx)("p",{style:{color:"#666",margin:0},children:"The request you would send to LiteLLM /chat/completions endpoint."})]}),(0,t.jsx)("textarea",{style:{flex:"1 1 auto",width:"100%",minHeight:"240px",padding:"16px",border:"1px solid #e8e8e8",borderRadius:"6px",fontFamily:"monospace",fontSize:"14px",resize:"none",marginBottom:"24px",overflow:"auto"},value:n,onChange:e=>d(e.target.value),onKeyDown:e=>{(e.metaKey||e.ctrlKey)&&"Enter"===e.key&&(e.preventDefault(),m())},placeholder:"Press Cmd/Ctrl + Enter to transform"}),(0,t.jsx)("div",{style:{display:"flex",justifyContent:"flex-end",marginTop:"auto"},children:(0,t.jsxs)(o.Button,{type:"primary",style:{backgroundColor:"#000",display:"flex",alignItems:"center",gap:"8px"},onClick:m,loading:p,children:[(0,t.jsx)("span",{children:"Transform"}),(0,t.jsx)("span",{children:"→"})]})})]}),(0,t.jsxs)("div",{style:{flex:"1 1 50%",display:"flex",flexDirection:"column",border:"1px solid #e8e8e8",borderRadius:"8px",padding:"24px",overflow:"hidden",maxHeight:"800px",minWidth:0},children:[(0,t.jsxs)("div",{style:{marginBottom:"24px"},children:[(0,t.jsx)("h2",{style:{fontSize:"24px",fontWeight:"bold",margin:"0 0 4px 0"},children:"Transformed Request"}),(0,t.jsx)("p",{style:{color:"#666",margin:0},children:"How LiteLLM transforms your request for the specified provider."}),(0,t.jsx)("br",{}),(0,t.jsx)("p",{style:{color:"#666",margin:0},className:"text-xs",children:"Note: Sensitive headers are not shown."})]}),(0,t.jsxs)("div",{style:{position:"relative",backgroundColor:"#f5f5f5",borderRadius:"6px",flex:"1 1 auto",display:"flex",flexDirection:"column",overflow:"hidden"},children:[(0,t.jsx)("pre",{style:{padding:"16px",fontFamily:"monospace",fontSize:"14px",margin:0,overflow:"auto",flex:"1 1 auto"},children:c||`curl -X POST \\ https://api.openai.com/v1/chat/completions \\ -H 'Authorization: Bearer sk-xxx' \\ -H 'Content-Type: application/json' \\ @@ -32,4 +32,4 @@ ${e} } ], "temperature": 0.7 - }'`}),(0,t.jsx)(s.Button,{type:"text",icon:(0,t.jsx)(o.CopyOutlined,{}),style:{position:"absolute",right:"8px",top:"8px"},size:"small",onClick:()=>{navigator.clipboard.writeText(c||""),a.default.success("Copied to clipboard")}})]})]})]}),(0,t.jsx)("div",{className:"mt-4 text-right w-full",children:(0,t.jsxs)("p",{className:"text-sm text-gray-500",children:["Found an error? File an issue"," ",(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm/issues",target:"_blank",rel:"noopener noreferrer",children:"here"}),"."]})})]})};var d=e.i(135214);e.s(["default",0,function(){let{accessToken:e}=(0,d.default)();return(0,t.jsx)(n,{accessToken:e})}],411929)}]); \ No newline at end of file + }'`}),(0,t.jsx)(o.Button,{type:"text",icon:(0,t.jsx)(s.CopyOutlined,{}),style:{position:"absolute",right:"8px",top:"8px"},size:"small",onClick:()=>{navigator.clipboard.writeText(c||""),i.default.success("Copied to clipboard")}})]})]})]}),(0,t.jsx)("div",{className:"mt-4 text-right w-full",children:(0,t.jsxs)("p",{className:"text-sm text-gray-500",children:["Found an error? File an issue"," ",(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm/issues",target:"_blank",rel:"noopener noreferrer",children:"here"}),"."]})})]})};var d=e.i(135214);e.s(["default",0,function(){let{accessToken:e}=(0,d.default)();return(0,t.jsx)(n,{accessToken:e})}],411929)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/069vv6t-agy4i.js b/litellm/proxy/_experimental/out/_next/static/chunks/069vv6t-agy4i.js new file mode 100644 index 00000000000..a155edc8359 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/069vv6t-agy4i.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,487486,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(552245),l=e.i(115504);let s=(0,l.cva)({base:"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [&>svg]:pointer-events-none [&>svg]:size-3",variants:{variant:{default:"bg-primary text-primary-foreground [a&]:hover:bg-primary/90",secondary:"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",destructive:"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",outline:"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",ghost:"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",link:"text-primary underline-offset-4 [a&]:hover:underline"}},defaultVariants:{variant:"default"}}),i=a.forwardRef(({className:e,variant:a="default",render:i,...d},n)=>{var o;return o={render:i??(0,t.jsx)("span",{}),ref:n,props:{"data-slot":"badge","data-variant":a,className:(0,l.cn)(s({variant:a}),e),...d}},(0,r.useRenderElement)(o.defaultTagName??"div",o,o)});i.displayName="Badge",e.s(["Badge",0,i],487486)},784774,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(115504);let l=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("div",{"data-slot":"table-container",className:"relative w-full overflow-x-auto",children:(0,t.jsx)("table",{ref:l,"data-slot":"table",className:(0,r.cn)("w-full caption-bottom text-sm",e),...a})}));l.displayName="Table";let s=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("thead",{ref:l,"data-slot":"table-header",className:(0,r.cn)("[&_tr]:border-b",e),...a}));s.displayName="TableHeader";let i=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("tbody",{ref:l,"data-slot":"table-body",className:(0,r.cn)("[&_tr:last-child]:border-0",e),...a}));i.displayName="TableBody";let d=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("tfoot",{ref:l,"data-slot":"table-footer",className:(0,r.cn)("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",e),...a}));d.displayName="TableFooter";let n=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("tr",{ref:l,"data-slot":"table-row",className:(0,r.cn)("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",e),...a}));n.displayName="TableRow";let o=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("th",{ref:l,"data-slot":"table-head",className:(0,r.cn)("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...a}));o.displayName="TableHead";let c=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("td",{ref:l,"data-slot":"table-cell",className:(0,r.cn)("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...a}));c.displayName="TableCell",a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("caption",{ref:l,"data-slot":"table-caption",className:(0,r.cn)("mt-4 text-sm text-muted-foreground",e),...a})).displayName="TableCaption",e.s(["Table",0,l,"TableBody",0,i,"TableCell",0,c,"TableFooter",0,d,"TableHead",0,o,"TableHeader",0,s,"TableRow",0,n])},302747,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(115504);let l=a.forwardRef(({className:e,...a},l)=>(0,t.jsx)("div",{ref:l,"data-slot":"skeleton",className:(0,r.cn)("animate-pulse rounded-md bg-accent",e),...a}));l.displayName="Skeleton",e.s(["Skeleton",0,l])},531278,e=>{"use strict";let t=(0,e.i(475254).default)("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);e.s(["Loader2",0,t],531278)},628851,e=>{"use strict";var t=e.i(843476),a=e.i(405033),r=e.i(271645),l=e.i(266027),s=e.i(912598),i=e.i(531278),d=e.i(727612);let n=(0,e.i(475254).default)("link",[["path",{d:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",key:"1cjeqo"}],["path",{d:"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",key:"19qd67"}]]);var o=e.i(487486),c=e.i(519455),u=e.i(302747),x=e.i(784774),m=e.i(868499),h=e.i(888259),f=e.i(602869);let b="mcp-user-credentials",p=({accessToken:e})=>{let a=(0,s.useQueryClient)(),[p,g]=(0,r.useState)(new Set),{data:v=[],isLoading:j}=(0,l.useQuery)({queryKey:[b,e],queryFn:()=>(0,f.listMCPUserCredentials)(e),enabled:!!e}),w=async t=>{g(e=>new Set(e).add(t));try{await (0,f.deleteMCPOAuthUserCredential)(e,t),a.setQueryData([b,e],e=>(e??[]).filter(e=>e.server_id!==t))}catch{h.default.error("Failed to revoke connection. Please try again.")}finally{g(e=>{let a=new Set(e);return a.delete(t),a})}},N=e=>e.alias||e.server_name||e.server_id;return(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)("h2",{className:"text-base font-semibold text-foreground mb-0.5",children:"App Credentials"}),(0,t.jsx)("p",{className:"text-sm text-muted-foreground m-0",children:"Your stored OAuth connections; used automatically in chat"})]}),j?(0,t.jsx)("div",{className:"rounded-lg border overflow-hidden",children:(0,t.jsxs)(x.Table,{children:[(0,t.jsx)(x.TableHeader,{children:(0,t.jsxs)(x.TableRow,{className:"bg-muted/50",children:[(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:"App"}),(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:"Connected"}),(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:"Status"}),(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground text-right",children:"Actions"})]})}),(0,t.jsx)(x.TableBody,{children:Array.from({length:3},(e,a)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(x.TableCell,{children:(0,t.jsx)(u.Skeleton,{className:"h-4 w-24"})}),(0,t.jsx)(x.TableCell,{children:(0,t.jsx)(u.Skeleton,{className:"h-4 w-16"})}),(0,t.jsx)(x.TableCell,{children:(0,t.jsx)(u.Skeleton,{className:"h-4 w-20"})}),(0,t.jsx)(x.TableCell,{className:"text-right",children:(0,t.jsx)(u.Skeleton,{className:"h-4 w-8 ml-auto"})})]},a))})]})}):0===v.length?(0,t.jsxs)("div",{className:"text-center text-muted-foreground text-sm py-12 border border-dashed rounded-lg",children:[(0,t.jsx)(n,{className:"h-6 w-6 mb-3 mx-auto text-muted-foreground/50"}),(0,t.jsx)("p",{className:"m-0",children:"No connections yet"}),(0,t.jsxs)("p",{className:"m-0 mt-1 text-xs",children:["Go to ",(0,t.jsx)("span",{className:"font-medium",children:"Integrations"})," and click"," ",(0,t.jsx)("span",{className:"font-medium",children:"Connect"})," to authorize an MCP server"]})]}):(0,t.jsx)("div",{className:"rounded-lg border overflow-hidden",children:(0,t.jsxs)(x.Table,{children:[(0,t.jsx)(x.TableHeader,{children:(0,t.jsxs)(x.TableRow,{className:"bg-muted/50",children:[(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:"App"}),(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:"Connected"}),(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:"Status"}),(0,t.jsx)(x.TableHead,{className:"text-[11px] font-medium uppercase tracking-wide text-muted-foreground text-right",children:"Actions"})]})}),(0,t.jsx)(x.TableBody,{children:v.map(e=>{let a=p.has(e.server_id),r=function(e){if(!e)return{text:"Does not expire",variant:"secondary"};try{let t=new Date(e).getTime()-Date.now();if(t<=0)return{text:"Expired",variant:"destructive"};let a=Math.floor(t/1e3),r=Math.floor(a/60),l=Math.floor(r/60),s=Math.floor(l/24);if(s>0)return{text:`Expires in ${s}d`,variant:"outline"};if(l>0)return{text:`Expires in ${l}h`,variant:"outline"};return{text:`Expires in ${r}m`,variant:"outline"}}catch{return{text:"",variant:"outline"}}}(e.expires_at);return(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(x.TableCell,{className:"text-sm font-medium",children:N(e)}),(0,t.jsx)(x.TableCell,{className:"text-sm text-muted-foreground",children:function(e){if(!e)return"";try{let t=new Date(e),a=Date.now()-t.getTime(),r=Math.floor(a/1e3);if(r<60)return"just now";let l=Math.floor(r/60);if(l<60)return`${l}m ago`;let s=Math.floor(l/60);if(s<24)return`${s}h ago`;return`${Math.floor(s/24)}d ago`}catch{return""}}(e.connected_at)||"—"}),(0,t.jsx)(x.TableCell,{children:(0,t.jsx)(o.Badge,{variant:r.variant,children:r.text})}),(0,t.jsx)(x.TableCell,{className:"text-right",children:(0,t.jsxs)(m.AlertDialog,{children:[(0,t.jsx)(m.AlertDialogTrigger,{render:(0,t.jsx)(c.Button,{variant:"outline",size:"icon-sm",disabled:a,title:"Revoke connection",className:"text-muted-foreground hover:text-destructive hover:border-destructive/50",children:a?(0,t.jsx)(i.Loader2,{className:"h-3.5 w-3.5 animate-spin"}):(0,t.jsx)(d.Trash2,{className:"h-3.5 w-3.5"})})}),(0,t.jsxs)(m.AlertDialogContent,{children:[(0,t.jsxs)(m.AlertDialogHeader,{children:[(0,t.jsx)(m.AlertDialogTitle,{children:"Revoke connection?"}),(0,t.jsxs)(m.AlertDialogDescription,{children:["This removes the stored OAuth credential for ",N(e),". You'll need to reconnect to use it in chat again."]})]}),(0,t.jsxs)(m.AlertDialogFooter,{children:[(0,t.jsx)(m.AlertDialogCancel,{children:"Cancel"}),(0,t.jsx)(m.AlertDialogAction,{variant:"destructive",onClick:()=>w(e.server_id),children:"Revoke"})]})]})]})})]},e.server_id)})})]})})]})};e.s(["default",0,function(){let{accessToken:e}=(0,a.useChatShell)();return(0,t.jsx)("div",{className:"flex-1 min-h-0 overflow-auto w-full py-8 px-8",children:(0,t.jsx)(p,{accessToken:e})})}],628851)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/06_vzvq0hkdw-.js b/litellm/proxy/_experimental/out/_next/static/chunks/06_vzvq0hkdw-.js new file mode 100644 index 00000000000..c52e103afe0 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/06_vzvq0hkdw-.js @@ -0,0 +1,23 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,321443,e=>{"use strict";var t=e.i(843476),n=e.i(271645),r=e.i(107233),i=e.i(664659),l=e.i(643531),o=e.i(37727),a=e.i(337822),s=e.i(302747),c=e.i(759684),u=e.i(793479),d=e.i(519455),f=e.i(888259),h=e.i(618566),m=e.i(405033),p=e.i(360179),g=e.i(195116),x=e.i(174886),k=e.i(788699),b=e.i(746798),v=e.i(204258),y=e.i(918789);function w(e,t){let n=String(e);if("string"!=typeof t)throw TypeError("Expected character");let r=0,i=n.indexOf(t);for(;-1!==i;)r++,i=n.indexOf(t,i+t.length);return r}var j=e.i(420061),C=e.i(997803),S=e.i(733644),N=e.i(457579);let E="phrasing",L=["autolink","link","image","label"];function A(e){this.enter({type:"link",title:null,url:"",children:[]},e)}function D(e){this.config.enter.autolinkProtocol.call(this,e)}function T(e){this.config.exit.autolinkProtocol.call(this,e)}function F(e){this.config.exit.data.call(this,e);let t=this.stack[this.stack.length-1];(0,j.ok)("link"===t.type),t.url="http://"+this.sliceSerialize(e)}function O(e){this.config.exit.autolinkEmail.call(this,e)}function z(e){this.exit(e)}function M(e){!function(e,t,n){let r=(0,N.convert)((n||{}).ignore||[]),i=function(e){let t=[];if(!Array.isArray(e))throw TypeError("Expected find and replace tuple or list of tuples");let n=!e[0]||Array.isArray(e[0])?e:[e],r=-1;for(;++r0?{type:"text",value:l}:void 0),!1===l?r.lastIndex=n+1:(a!==n&&u.push({type:"text",value:e.value.slice(a,n)}),Array.isArray(l)?u.push(...l):l&&u.push(l),a=n+d[0].length,c=!0),!r.global)break;d=r.exec(e.value)}return c?(a?\]}]+$/.exec(e);if(!t)return[e,void 0];e=e.slice(0,t.index);let n=t[0],r=n.indexOf(")"),i=w(e,"("),l=w(e,")");for(;-1!==r&&i>l;)e+=n.slice(0,r+1),r=(n=n.slice(r+1)).indexOf(")"),l++;return[e,n]}(n+r);if(!a[0])return!1;let s={type:"link",title:null,url:o+t+a[0],children:[{type:"text",value:t+a[0]}]};return a[1]?[s,{type:"text",value:a[1]}]:s}function P(e,t,n,r){return!(!_(r,!0)||/[-\d_]$/.test(n))&&{type:"link",title:null,url:"mailto:"+t+"@"+n,children:[{type:"text",value:t+"@"+n}]}}function _(e,t){let n=e.input.charCodeAt(e.index-1);return(0===e.index||(0,C.unicodeWhitespace)(n)||(0,C.unicodePunctuation)(n))&&(!t||47!==n)}var I=e.i(431745);function H(){this.buffer()}function B(e){this.enter({type:"footnoteReference",identifier:"",label:""},e)}function $(){this.buffer()}function W(e){this.enter({type:"footnoteDefinition",identifier:"",label:"",children:[]},e)}function q(e){let t=this.resume(),n=this.stack[this.stack.length-1];(0,j.ok)("footnoteReference"===n.type),n.identifier=(0,I.normalizeIdentifier)(this.sliceSerialize(e)).toLowerCase(),n.label=t}function U(e){this.exit(e)}function V(e){let t=this.resume(),n=this.stack[this.stack.length-1];(0,j.ok)("footnoteDefinition"===n.type),n.identifier=(0,I.normalizeIdentifier)(this.sliceSerialize(e)).toLowerCase(),n.label=t}function K(e){this.exit(e)}function G(e,t,n,r){let i=n.createTracker(r),l=i.move("[^"),o=n.enter("footnoteReference"),a=n.enter("reference");return l+=i.move(n.safe(n.associationId(e),{after:"]",before:l})),a(),o(),l+=i.move("]")}function J(e,t,n){return 0===t?e:Y(e,t,n)}function Y(e,t,n){return(n?"":" ")+e}G.peek=function(){return"["};let Z=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];function Q(e){this.enter({type:"delete",children:[]},e)}function X(e){this.exit(e)}function ee(e,t,n,r){let i=n.createTracker(r),l=n.enter("strikethrough"),o=i.move("~~");return o+=n.containerPhrasing(e,{...i.current(),before:o,after:"~"}),o+=i.move("~~"),l(),o}function et(e){return e.length}function en(e){let t="string"==typeof e?e.codePointAt(0):0;return 67===t||99===t?99:76===t||108===t?108:114*(82===t||114===t)}ee.peek=function(){return"~"};var er=e.i(682523);e.i(784801);e.i(900065);function ei(e,t,n){let r=e.value||"",i="`",l=-1;for(;RegExp("(^|[^`])"+i+"([^`]|$)").test(r);)i+="`";for(/[^ \r\n]/.test(r)&&(/^[ \r\n]/.test(r)&&/[ \r\n]$/.test(r)||/^`|`$/.test(r))&&(r=" "+r+" ");++l-1?t.start:1)+(!1===n.options.incrementListMarker?0:t.children.indexOf(e))+l);let o=l.length+1;("tab"===i||"mixed"===i&&(t&&"list"===t.type&&t.spread||e.spread))&&(o=4*Math.ceil(o/4));let a=n.createTracker(r);a.move(l+" ".repeat(o-l.length)),a.shift(o);let s=n.enter("listItem"),c=n.indentLines(n.containerFlow(e,a.current()),function(e,t,n){return t?(n?"":" ".repeat(o))+e:(n?l:l+" ".repeat(o-l.length))+e});return s(),c};function eo(e){let t=e._align;(0,j.ok)(t,"expected `_align` on table"),this.enter({type:"table",align:t.map(function(e){return"none"===e?null:e}),children:[]},e),this.data.inTable=!0}function ea(e){this.exit(e),this.data.inTable=void 0}function es(e){this.enter({type:"tableRow",children:[]},e)}function ec(e){this.exit(e)}function eu(e){this.enter({type:"tableCell",children:[]},e)}function ed(e){let t=this.resume();this.data.inTable&&(t=t.replace(/\\([\\|])/g,ef));let n=this.stack[this.stack.length-1];(0,j.ok)("inlineCode"===n.type),n.value=t,this.exit(e)}function ef(e,t){return"|"===t?t:e}function eh(e){let t=this.stack[this.stack.length-2];(0,j.ok)("listItem"===t.type),t.checked="taskListCheckValueChecked"===e.type}function em(e){let t=this.stack[this.stack.length-2];if(t&&"listItem"===t.type&&"boolean"==typeof t.checked){let e=this.stack[this.stack.length-1];(0,j.ok)("paragraph"===e.type);let n=e.children[0];if(n&&"text"===n.type){let r,i=t.children,l=-1;for(;++l0&&!n&&(e[e.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}eS[43]=eC,eS[45]=eC,eS[46]=eC,eS[95]=eC,eS[72]=[eC,ej],eS[104]=[eC,ej],eS[87]=[eC,ew],eS[119]=[eC,ew];var eF=e.i(653161),eO=e.i(204108);let ez={tokenize:function(e,t,n){let r=this;return(0,eO.factorySpace)(e,function(e){let i=r.events[r.events.length-1];return i&&"gfmFootnoteDefinitionIndent"===i[1].type&&4===i[2].sliceSerialize(i[1],!0).length?t(e):n(e)},"gfmFootnoteDefinitionIndent",5)},partial:!0};function eM(e,t,n){let r,i=this,l=i.events.length,o=i.parser.gfmFootnotes||(i.parser.gfmFootnotes=[]);for(;l--;){let e=i.events[l][1];if("labelImage"===e.type){r=e;break}if("gfmFootnoteCall"===e.type||"labelLink"===e.type||"label"===e.type||"image"===e.type||"link"===e.type)break}return function(l){if(!r||!r._balanced)return n(l);let a=(0,I.normalizeIdentifier)(i.sliceSerialize({start:r.end,end:i.now()}));return 94===a.codePointAt(0)&&o.includes(a.slice(1))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(l),e.exit("gfmFootnoteCallLabelMarker"),t(l)):n(l)}}function eR(e,t){let n=e.length;for(;n--;)if("labelImage"===e[n][1].type&&"enter"===e[n][0]){e[n][1];break}e[n+1][1].type="data",e[n+3][1].type="gfmFootnoteCallLabelMarker";let r={type:"gfmFootnoteCall",start:Object.assign({},e[n+3][1].start),end:Object.assign({},e[e.length-1][1].end)},i={type:"gfmFootnoteCallMarker",start:Object.assign({},e[n+3][1].end),end:Object.assign({},e[n+3][1].end)};i.end.column++,i.end.offset++,i.end._bufferIndex++;let l={type:"gfmFootnoteCallString",start:Object.assign({},i.end),end:Object.assign({},e[e.length-1][1].start)},o={type:"chunkString",contentType:"string",start:Object.assign({},l.start),end:Object.assign({},l.end)},a=[e[n+1],e[n+2],["enter",r,t],e[n+3],e[n+4],["enter",i,t],["exit",i,t],["enter",l,t],["enter",o,t],["exit",o,t],["exit",l,t],e[e.length-2],e[e.length-1],["exit",r,t]];return e.splice(n,e.length-n+1,...a),e}function eP(e,t,n){let r,i=this,l=i.parser.gfmFootnotes||(i.parser.gfmFootnotes=[]),o=0;return function(t){return e.enter("gfmFootnoteCall"),e.enter("gfmFootnoteCallLabelMarker"),e.consume(t),e.exit("gfmFootnoteCallLabelMarker"),a};function a(t){return 94!==t?n(t):(e.enter("gfmFootnoteCallMarker"),e.consume(t),e.exit("gfmFootnoteCallMarker"),e.enter("gfmFootnoteCallString"),e.enter("chunkString").contentType="string",s)}function s(a){if(o>999||93===a&&!r||null===a||91===a||(0,C.markdownLineEndingOrSpace)(a))return n(a);if(93===a){e.exit("chunkString");let r=e.exit("gfmFootnoteCallString");return l.includes((0,I.normalizeIdentifier)(i.sliceSerialize(r)))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(a),e.exit("gfmFootnoteCallLabelMarker"),e.exit("gfmFootnoteCall"),t):n(a)}return(0,C.markdownLineEndingOrSpace)(a)||(r=!0),o++,e.consume(a),92===a?c:s}function c(t){return 91===t||92===t||93===t?(e.consume(t),o++,s):s(t)}}function e_(e,t,n){let r,i,l=this,o=l.parser.gfmFootnotes||(l.parser.gfmFootnotes=[]),a=0;return function(t){return e.enter("gfmFootnoteDefinition")._container=!0,e.enter("gfmFootnoteDefinitionLabel"),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(t),e.exit("gfmFootnoteDefinitionLabelMarker"),s};function s(t){return 94===t?(e.enter("gfmFootnoteDefinitionMarker"),e.consume(t),e.exit("gfmFootnoteDefinitionMarker"),e.enter("gfmFootnoteDefinitionLabelString"),e.enter("chunkString").contentType="string",c):n(t)}function c(t){if(a>999||93===t&&!i||null===t||91===t||(0,C.markdownLineEndingOrSpace)(t))return n(t);if(93===t){e.exit("chunkString");let n=e.exit("gfmFootnoteDefinitionLabelString");return r=(0,I.normalizeIdentifier)(l.sliceSerialize(n)),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(t),e.exit("gfmFootnoteDefinitionLabelMarker"),e.exit("gfmFootnoteDefinitionLabel"),d}return(0,C.markdownLineEndingOrSpace)(t)||(i=!0),a++,e.consume(t),92===t?u:c}function u(t){return 91===t||92===t||93===t?(e.consume(t),a++,c):c(t)}function d(t){return 58===t?(e.enter("definitionMarker"),e.consume(t),e.exit("definitionMarker"),o.includes(r)||o.push(r),(0,eO.factorySpace)(e,f,"gfmFootnoteDefinitionWhitespace")):n(t)}function f(e){return t(e)}}function eI(e,t,n){return e.check(eF.blankLine,t,e.attempt(ez,t,n))}function eH(e){e.exit("gfmFootnoteDefinition")}var eB=e.i(938402),e$=e.i(810291);class eW{constructor(){this.map=[]}add(e,t,n){!function(e,t,n,r){let i=0;if(0!==n||0!==r.length){for(;i0;)t-=1,n.push(e.slice(this.map[t][0]+this.map[t][1]),this.map[t][2]),e.length=this.map[t][0];n.push(e.slice()),e.length=0;let r=n.pop();for(;r;){for(let t of r)e.push(t);r=n.pop()}this.map.length=0}}function eq(e,t,n){let r,i=this,l=0,o=0;return function(e){let t=i.events.length-1;for(;t>-1;){let e=i.events[t][1].type;if("lineEnding"===e||"linePrefix"===e)t--;else break}let r=t>-1?i.events[t][1].type:null,l="tableHead"===r||"tableRow"===r?k:a;return l===k&&i.parser.lazy[i.now().line]?n(e):l(e)};function a(t){var n;return e.enter("tableHead"),e.enter("tableRow"),124===(n=t)||(r=!0,o+=1),s(n)}function s(t){return null===t?n(t):(0,C.markdownLineEnding)(t)?o>1?(o=0,i.interrupt=!0,e.exit("tableRow"),e.enter("lineEnding"),e.consume(t),e.exit("lineEnding"),d):n(t):(0,C.markdownSpace)(t)?(0,eO.factorySpace)(e,s,"whitespace")(t):(o+=1,r&&(r=!1,l+=1),124===t)?(e.enter("tableCellDivider"),e.consume(t),e.exit("tableCellDivider"),r=!0,s):(e.enter("data"),c(t))}function c(t){return null===t||124===t||(0,C.markdownLineEndingOrSpace)(t)?(e.exit("data"),s(t)):(e.consume(t),92===t?u:c)}function u(t){return 92===t||124===t?(e.consume(t),c):c(t)}function d(t){return(i.interrupt=!1,i.parser.lazy[i.now().line])?n(t):(e.enter("tableDelimiterRow"),r=!1,(0,C.markdownSpace)(t))?(0,eO.factorySpace)(e,f,"linePrefix",i.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(t):f(t)}function f(t){return 45===t||58===t?m(t):124===t?(r=!0,e.enter("tableCellDivider"),e.consume(t),e.exit("tableCellDivider"),h):n(t)}function h(t){return(0,C.markdownSpace)(t)?(0,eO.factorySpace)(e,m,"whitespace")(t):m(t)}function m(t){return 58===t?(o+=1,r=!0,e.enter("tableDelimiterMarker"),e.consume(t),e.exit("tableDelimiterMarker"),p):45===t?(o+=1,p(t)):null===t||(0,C.markdownLineEnding)(t)?x(t):n(t)}function p(t){return 45===t?(e.enter("tableDelimiterFiller"),function t(n){return 45===n?(e.consume(n),t):58===n?(r=!0,e.exit("tableDelimiterFiller"),e.enter("tableDelimiterMarker"),e.consume(n),e.exit("tableDelimiterMarker"),g):(e.exit("tableDelimiterFiller"),g(n))}(t)):n(t)}function g(t){return(0,C.markdownSpace)(t)?(0,eO.factorySpace)(e,x,"whitespace")(t):x(t)}function x(i){if(124===i)return f(i);if(null===i||(0,C.markdownLineEnding)(i))return r&&l===o?(e.exit("tableDelimiterRow"),e.exit("tableHead"),t(i)):n(i);return n(i)}function k(t){return e.enter("tableRow"),b(t)}function b(n){return 124===n?(e.enter("tableCellDivider"),e.consume(n),e.exit("tableCellDivider"),b):null===n||(0,C.markdownLineEnding)(n)?(e.exit("tableRow"),t(n)):(0,C.markdownSpace)(n)?(0,eO.factorySpace)(e,b,"whitespace")(n):(e.enter("data"),v(n))}function v(t){return null===t||124===t||(0,C.markdownLineEndingOrSpace)(t)?(e.exit("data"),b(t)):(e.consume(t),92===t?y:v)}function y(t){return 92===t||124===t?(e.consume(t),v):v(t)}}function eU(e,t){let n,r,i,l=-1,o=!0,a=0,s=[0,0,0,0],c=[0,0,0,0],u=!1,d=0,f=new eW;for(;++ln[2]+1){let t=n[2]+1,r=n[3]-n[2]-1;e.add(t,r,[])}}e.add(n[3]+1,0,[["exit",o,t]])}return void 0!==i&&(l.end=Object.assign({},eG(t.events,i)),e.add(i,0,[["exit",l,t]]),l=void 0),l}function eK(e,t,n,r,i){let l=[],o=eG(t.events,n);i&&(i.end=Object.assign({},o),l.push(["exit",i,t])),r.end=Object.assign({},o),l.push(["exit",r,t]),e.add(n+1,0,l)}function eG(e,t){let n=e[t],r="enter"===n[0]?"start":"end";return n[1][r]}let eJ={name:"tasklistCheck",tokenize:function(e,t,n){let r=this;return function(t){return null===r.previous&&r._gfmTasklistFirstContentOfListItem?(e.enter("taskListCheck"),e.enter("taskListCheckMarker"),e.consume(t),e.exit("taskListCheckMarker"),i):n(t)};function i(t){return(0,C.markdownLineEndingOrSpace)(t)?(e.enter("taskListCheckValueUnchecked"),e.consume(t),e.exit("taskListCheckValueUnchecked"),l):88===t||120===t?(e.enter("taskListCheckValueChecked"),e.consume(t),e.exit("taskListCheckValueChecked"),l):n(t)}function l(t){return 93===t?(e.enter("taskListCheckMarker"),e.consume(t),e.exit("taskListCheckMarker"),e.exit("taskListCheck"),o):n(t)}function o(r){return(0,C.markdownLineEnding)(r)?t(r):(0,C.markdownSpace)(r)?e.check({tokenize:eY},t,n)(r):n(r)}}};function eY(e,t,n){return(0,eO.factorySpace)(e,function(e){return null===e?n(e):t(e)},"whitespace")}let eZ={};function eQ(e){var t;let n,r,i,l=e||eZ,o=this.data(),a=o.micromarkExtensions||(o.micromarkExtensions=[]),s=o.fromMarkdownExtensions||(o.fromMarkdownExtensions=[]),c=o.toMarkdownExtensions||(o.toMarkdownExtensions=[]);a.push((t=l,(0,eg.combineExtensions)([{text:eS},{document:{91:{name:"gfmFootnoteDefinition",tokenize:e_,continuation:{tokenize:eI},exit:eH}},text:{91:{name:"gfmFootnoteCall",tokenize:eP},93:{name:"gfmPotentialFootnoteCall",add:"after",tokenize:eM,resolveTo:eR}}},(n=(t||{}).singleTilde,r={name:"strikethrough",tokenize:function(e,t,r){let i=this.previous,l=this.events,o=0;return function(a){return 126===i&&"characterEscape"!==l[l.length-1][1].type?r(a):(e.enter("strikethroughSequenceTemporary"),function l(a){let s=(0,er.classifyCharacter)(i);if(126===a)return o>1?r(a):(e.consume(a),o++,l);if(o<2&&!n)return r(a);let c=e.exit("strikethroughSequenceTemporary"),u=(0,er.classifyCharacter)(a);return c._open=!u||2===u&&!!s,c._close=!s||2===s&&!!u,t(a)}(a))}},resolveAll:function(e,t){let n=-1;for(;++n0&&(l.shift(4),o+=l.move((i?"\n":" ")+n.indentLines(n.containerFlow(e,l.current()),i?Y:J))),a(),o},footnoteReference:G},unsafe:[{character:"[",inConstruct:["label","phrasing","reference"]}]}),{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:Z}],handlers:{delete:ee}},function(e){let t=e||{},n=t.tableCellPadding,r=t.tablePipeAlign,i=t.stringLength,l=n?" ":"|";return{unsafe:[{character:"\r",inConstruct:"tableCell"},{character:"\n",inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{inlineCode:function(e,t,n){let r=ei(e,t,n);return n.stack.includes("tableCell")&&(r=r.replace(/\|/g,"\\$&")),r},table:function(e,t,n,r){return a(function(e,t,n){let r=e.children,i=-1,l=[],o=t.enter("table");for(;++ic&&(c=e[u].length);++ls[l])&&(s[l]=e)}t.push(o)}o[u]=t,a[u]=r}let f=-1;if("object"==typeof r&&"length"in r)for(;++fs[f]&&(s[f]=i),m[f]=i),h[f]=o}o.splice(1,0,h),a.splice(1,0,m),u=-1;let p=[];for(;++u{a&&f.current&&(f.current.focus(),f.current.selectionStart=f.current.value.length)},[a]),(0,n.useEffect)(()=>{let e=f.current;e&&(e.style.height="auto",e.style.height=`${e.scrollHeight}px`)},[c,a]);let h=()=>{let t=c.trim();t&&t!==e.content&&r&&r(e.id,t),s(!1)};return a?(0,t.jsx)("div",{className:"flex flex-col items-end",children:(0,t.jsxs)("div",{className:"w-[72%] bg-background border-2 border-primary rounded-xl overflow-hidden shadow-[0_0_0_3px_rgba(var(--primary)/0.1)]",children:[(0,t.jsx)("textarea",{ref:f,value:c,onChange:e=>u(e.target.value),onKeyDown:t=>{"Enter"!==t.key||t.shiftKey||(t.preventDefault(),h()),"Escape"===t.key&&(u(e.content),s(!1))},className:"w-full px-3.5 py-2.5 border-none outline-none resize-none text-sm leading-relaxed text-foreground font-[inherit] bg-transparent box-border min-h-[40px]"}),(0,t.jsxs)("div",{className:"flex justify-end gap-2 px-2.5 py-1.5 border-t",children:[(0,t.jsx)(d.Button,{variant:"outline",size:"sm",onClick:()=>{u(e.content),s(!1)},children:"Cancel"}),(0,t.jsx)(d.Button,{size:"sm",onClick:h,disabled:!c.trim(),children:"Save & Send"})]})]})}):(0,t.jsxs)("div",{className:"flex flex-col items-end w-full",onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),children:[(0,t.jsxs)("div",{className:"flex items-end gap-1.5 max-w-[72%]",children:[l&&!i&&r&&(0,t.jsx)(b.TooltipProvider,{delay:300,children:(0,t.jsxs)(b.Tooltip,{children:[(0,t.jsx)(b.TooltipTrigger,{render:(0,t.jsx)(d.Button,{variant:"ghost",size:"icon-xs",onClick:()=>{u(e.content),s(!0)},className:"text-muted-foreground hover:text-foreground shrink-0",children:(0,t.jsx)(k.Pencil,{className:"size-3.5"})})}),(0,t.jsx)(b.TooltipContent,{children:(0,t.jsx)("p",{children:"Edit message"})})]})}),(0,t.jsx)("div",{className:"bg-muted rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed whitespace-pre-wrap break-words text-foreground",children:e.content})]}),(0,t.jsx)("span",{className:"text-[11px] text-muted-foreground mt-1",children:e4(e.timestamp)})]})}function e6({message:e,isLastMessage:r,isStreaming:i,isTypingIndicator:l,mcpEvents:o}){let[a,s]=(0,n.useState)(0),c=(0,n.useRef)(i);(0,n.useEffect)(()=>{c.current&&!i&&s(e=>e+1),c.current=i},[i]);let u=r&&i&&!e.reasoningContent,d=!!e.reasoningContent||u;if(l)return(0,t.jsx)("div",{className:"flex flex-col items-start",children:(0,t.jsx)("div",{className:"flex items-center gap-1 px-1 py-2.5",children:(0,t.jsx)(te,{})})});let f=e.content,h=!1;return f.endsWith("[stopped]")&&(f=f.slice(0,-9),h=!0),(0,t.jsxs)("div",{className:"flex flex-col items-start max-w-[80%]",children:[d&&(u?(0,t.jsx)(e7,{}):(0,t.jsx)(e1.default,{reasoningContent:e.reasoningContent},a)),(0,t.jsxs)("div",{className:"text-sm leading-[1.7] text-foreground break-words",children:[(0,t.jsx)(y.default,{remarkPlugins:[eQ],components:{code:e5},children:f}),h&&(0,t.jsx)("span",{className:"text-muted-foreground italic",children:" [stopped]"})]}),(0,t.jsx)(e8,{text:f}),o&&o.length>0&&(0,t.jsx)("div",{className:"mt-2 max-w-full",children:(0,t.jsx)(e2.default,{events:o})})]})}function e8({text:e}){let[r,i]=(0,n.useState)(!1);return(0,t.jsx)("div",{className:"flex items-center gap-1 mt-1.5",children:(0,t.jsx)(b.TooltipProvider,{delay:300,children:(0,t.jsxs)(b.Tooltip,{children:[(0,t.jsx)(b.TooltipTrigger,{render:(0,t.jsx)(d.Button,{variant:"ghost",size:"icon-xs",onClick:()=>{navigator.clipboard.writeText(e).then(()=>{i(!0),setTimeout(()=>i(!1),2e3)}).catch(()=>{})},className:r?"text-emerald-600":"text-muted-foreground hover:text-foreground",children:r?(0,t.jsx)(l.Check,{className:"size-3.5"}):(0,t.jsx)(x.Copy,{className:"size-3.5"})})}),(0,t.jsx)(b.TooltipContent,{children:(0,t.jsx)("p",{children:r?"Copied!":"Copy"})})]})})})}function e7(){return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("style",{children:` + @keyframes thinking-pulse { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 1; } + } + .chat-thinking-text { + animation: thinking-pulse 1.4s ease-in-out infinite; + } + `}),(0,t.jsx)("div",{className:"inline-flex items-center gap-1.5 px-2.5 mb-2 bg-muted/50 border rounded-lg text-xs text-muted-foreground",children:(0,t.jsx)("span",{className:"chat-thinking-text py-1",children:"Thinking..."})})]})}function te(){return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("style",{children:` + @keyframes chat-typing-bounce { + 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } + 30% { transform: translateY(-4px); opacity: 1; } + } + .chat-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background-color: var(--color-muted-foreground); + animation: chat-typing-bounce 1.2s ease-in-out infinite; + } + .chat-dot:nth-child(2) { animation-delay: 0.2s; } + .chat-dot:nth-child(3) { animation-delay: 0.4s; } + `}),(0,t.jsx)("div",{className:"chat-dot"}),(0,t.jsx)("div",{className:"chat-dot"}),(0,t.jsx)("div",{className:"chat-dot"})]})}function tt({message:e}){let r=e.toolArgs?function e(t){let n={};for(let[r,i]of Object.entries(t))e3.test(r)?n[r]="[redacted]":Array.isArray(i)?n[r]=i.map(t=>null===t||"object"!=typeof t||Array.isArray(t)?t:e(t)):null!==i&&"object"==typeof i?n[r]=e(i):n[r]=i;return n}(e.toolArgs):void 0,[i,l]=(0,n.useState)(!1);return(0,t.jsxs)("div",{className:"max-w-[80%]",children:[(0,t.jsxs)(v.Collapsible,{open:i,onOpenChange:l,children:[(0,t.jsxs)(v.CollapsibleTrigger,{className:"flex items-center gap-1.5 text-[13px] px-3 py-2 border rounded-lg bg-muted/50 hover:bg-muted transition-colors w-full text-left",children:[(0,t.jsx)(g.Wrench,{className:"h-3.5 w-3.5 text-muted-foreground"}),(0,t.jsx)("span",{className:"font-medium text-foreground",children:e.toolName??"Tool call"})]}),(0,t.jsxs)(v.CollapsibleContent,{className:"border border-t-0 rounded-b-lg px-3 py-2 bg-muted/30",children:[void 0!==r&&(0,t.jsxs)("div",{className:e.toolResult?"mb-3":"",children:[(0,t.jsx)("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-1",children:"Arguments"}),(0,t.jsx)("pre",{className:"m-0 p-2 bg-muted rounded-md text-xs font-mono whitespace-pre-wrap break-words text-foreground",children:JSON.stringify(r,null,2)})]}),e.toolResult&&(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-1",children:"Result"}),(0,t.jsx)("div",{className:"text-[13px] text-foreground whitespace-pre-wrap break-words font-mono",children:e.toolResult})]})]})]}),(0,t.jsx)("div",{className:"text-[11px] text-muted-foreground mt-1",children:e4(e.timestamp)})]})}let tn=({messages:e,isStreaming:n,onEditMessage:r})=>{let i=e.length-1,l=e[i]??null,o=n&&null!==l&&"assistant"===l.role&&""===l.content;return(0,t.jsx)("div",{className:"flex flex-col gap-4",children:e.map((e,l)=>{let a=l===i;return"user"===e.role?(0,t.jsx)(e9,{message:e,onEdit:r,isStreaming:n},e.id):"tool"===e.role?(0,t.jsx)(tt,{message:e},e.id):(0,t.jsx)(e6,{message:e,isLastMessage:a,isStreaming:n,isTypingIndicator:a&&o,mcpEvents:e.mcpEvents},e.id)})})};var tr=e.i(531278),ti=e.i(699375),tl=e.i(602869);let to=({accessToken:e,selectedServers:r,onChange:i})=>{let[l,o]=(0,n.useState)([]),[a,c]=(0,n.useState)(!0),[u,d]=(0,n.useState)(new Set);(0,n.useEffect)(()=>{let t=!1;return(async()=>{c(!0);try{let n=await (0,tl.fetchMCPServers)(e);if(t)return;let r=Array.isArray(n)?n:n?.data??[];o(r)}catch{t||o([])}finally{t||c(!1)}})(),()=>{t=!0}},[e]);let h=async(t,n)=>{if(!n)return void i(r.filter(e=>e!==t));d(e=>new Set(e).add(t));try{let n=await (0,tl.listMCPTools)(e,t);if(n?.error)return void f.default.warning(`Could not load tools for ${t} \u2014 it will be excluded from this message.`);i([...r,t])}catch{f.default.warning(`Could not load tools for ${t} \u2014 it will be excluded from this message.`)}finally{d(e=>{let n=new Set(e);return n.delete(t),n})}};return(0,t.jsx)("div",{className:"max-w-[320px] max-h-[400px] overflow-y-auto py-2",children:a?(0,t.jsx)("div",{className:"flex flex-col gap-1",children:Array.from({length:3}).map((e,n)=>(0,t.jsxs)("div",{className:"flex items-center justify-between px-3 py-2 gap-3",children:[(0,t.jsxs)("div",{className:"flex items-start gap-3 flex-1 min-w-0",children:[(0,t.jsx)(s.Skeleton,{className:"h-6 w-6 rounded-md shrink-0"}),(0,t.jsxs)("div",{className:"flex flex-col gap-1.5 flex-1 min-w-0",children:[(0,t.jsx)(s.Skeleton,{className:"h-3.5 w-24"}),(0,t.jsx)(s.Skeleton,{className:"h-3 w-32"})]})]}),(0,t.jsx)(s.Skeleton,{className:"h-3.5 w-6 rounded-full shrink-0"})]},n))}):0===l.length?(0,t.jsx)("div",{className:"px-3 py-4 text-muted-foreground text-[13px] text-center",children:"No MCP servers configured"}):l.map(e=>{let n=e.server_name??e.alias??e.server_id,i=r.includes(n),l=u.has(n);return(0,t.jsxs)("div",{className:"flex items-start justify-between px-3 py-2 gap-3",children:[e.mcp_info?.logo_url&&(0,t.jsx)("img",{src:e.mcp_info.logo_url,alt:`${n} logo`,className:"w-6 h-6 rounded-md object-contain shrink-0 mt-0.5",onError:e=>{e.target.style.display="none"}}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("div",{className:"font-medium text-[13px] text-foreground truncate",children:n}),e.description&&(0,t.jsx)("div",{className:"text-xs text-muted-foreground mt-0.5 truncate",children:e.description})]}),(0,t.jsx)("div",{className:"relative shrink-0",children:l?(0,t.jsx)(tr.Loader2,{className:"h-4 w-4 animate-spin text-muted-foreground"}):(0,t.jsx)(ti.Switch,{checked:i,onCheckedChange:e=>h(n,e),className:"scale-75"})})]},e.server_id)})})};var ta=e.i(695411),ts=e.i(459161),tc=e.i(916925);let tu=["Write","Learn","Code","Brainstorm"],td="litellm_chat_selected_model";function tf(){let e=new Date().getHours();return e>=5&&e<12?"Good morning":e>=12&&e<17?"Good afternoon":"Good evening"}function th(e){if(!e)return"";let t=e.toLowerCase(),n=t.indexOf("/");return n>0?t.slice(0,n):t.includes("claude")?"anthropic":t.includes("gemini")?"gemini":t.includes("gpt")||t.includes("chatgpt")||/^o[0-9]/.test(t)?"openai":t.includes("mistral")||t.includes("codestral")?"mistral":t.includes("llama")?"meta_llama":t.includes("deepseek")?"deepseek":t.includes("grok")?"xai":t.includes("command")?"cohere":t.includes("nova")||t.includes("titan")?"bedrock":""}e.s(["default",0,function(){let e=(0,h.useRouter)(),{accessToken:g,userId:x,userEmail:k,selectedMCPServers:b,setSelectedMCPServers:v,activeConversationId:y,activeConversation:w,storageUnavailable:j,staleId:C,createConversation:S,appendMessage:N,updateLastAssistantMessage:E,truncateFromMessage:L}=(0,m.useChatShell)();(0,n.useRef)(null!==y);let[A,D]=(0,n.useState)(null),[T,F]=(0,n.useState)([]),[O,z]=(0,n.useState)(!0),[M,R]=(0,n.useState)(!1),[P,_]=(0,n.useState)(""),[I,H]=(0,n.useState)(null),[B,$]=(0,n.useState)(y),[W,q]=(0,n.useState)(!1),[U,V]=(0,n.useState)(""),[K,G]=(0,n.useState)(!1),[J,Y]=(0,n.useState)(!1),Z=(0,n.useRef)(null),Q=(0,n.useRef)(null),X=(0,n.useRef)(null),[ee,et]=(0,n.useState)(!1),en=(0,n.useRef)(null);(0,n.useEffect)(()=>{C&&e.replace(p.CHAT_ROUTES.chats)},[C,e]),(0,n.useEffect)(()=>{g&&(0,ta.fetchAvailableModels)(g).then(e=>{let t=(e||[]).map(e=>e.model_group??"").filter(Boolean);F(t);try{let e=localStorage.getItem(td);if(e&&t.includes(e))return void D(e)}catch{}t.length>0&&(D(t[0]),localStorage.setItem(td,t[0]))}).catch(()=>f.default.error("Could not load models")).finally(()=>z(!1))},[g]),y!==B&&($(y),H(null));let er=(0,n.useCallback)(e=>{D(e),localStorage.setItem(td,e),R(!1),_("")},[]),ei=(0,n.useCallback)(async(t,n)=>{let r=t.trim();if(!r||!A||W)return;V("");let i=y;i||(i=S(A),H(null),e.push(`${p.CHAT_ROUTES.chats}?id=${i}`)),N(i,{role:"user",content:r}),N(i,{role:"assistant",content:""}),q(!0),Z.current=new AbortController,n&&H(null);let l=n?null:I,o=n?[...n,{role:"user",content:r}]:l?[{role:"user",content:r}]:[...(w?.messages??[]).filter(e=>"user"===e.role||"assistant"===e.role).map(e=>({role:e.role,content:e.content})),{role:"user",content:r}],a="",s="",c=[],u=!1;try{await (0,ts.makeOpenAIResponsesRequest)(o,(e,t)=>{a+=t,E(i,{content:a})},A,g,void 0,Z.current.signal,e=>{s+=e,E(i,{reasoningContent:s})},void 0,void 0,void 0,void 0,void 0,void 0,b.length>0?b:void 0,l,e=>H(e),e=>{c.push(e)}),u=!0}catch(e){e instanceof Error&&"AbortError"===e.name?E(i,{content:a+" [stopped]"}):E(i,{content:"[Something went wrong. The partial response has been saved.]"})}finally{c.length>0&&u&&E(i,{mcpEvents:c}),q(!1),Z.current=null}},[y,w,A,b,g,S,N,E,e,W,I]),el=(0,n.useCallback)(()=>{Z.current?.abort()},[]),eo=(0,n.useCallback)((e,t)=>{if(!y||W)return;let n=w?.messages??[],r=n.findIndex(t=>t.id===e),i=(-1===r?n:n.slice(0,r)).filter(e=>"user"===e.role||"assistant"===e.role).map(e=>({role:e.role,content:e.content}));L(y,e),ei(t,i)},[y,W,w,L,ei]),ea=e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),ei(U))};(0,n.useEffect)(()=>{let e=Q.current;e&&(e.style.height="auto",e.style.height=`${Math.min(e.scrollHeight,180)}px`)},[U]),(0,n.useEffect)(()=>{let e=X.current;if(!e)return;let t=()=>{et(e.scrollHeight-e.scrollTop-e.clientHeight>120),null!==en.current&&(en.current=e.scrollTop)};return e.addEventListener("scroll",t,{passive:!0}),()=>e.removeEventListener("scroll",t)},[w]),(0,n.useEffect)(()=>{let e=X.current;W?en.current=e?.scrollTop??0:en.current=null},[W]),(0,n.useLayoutEffect)(()=>{if(null===en.current)return;let e=X.current;e&&(e.scrollTop=en.current)});let es=(0,n.useRef)(0);(0,n.useLayoutEffect)(()=>{let e=w?.messages?.length??0,t=es.current;if(es.current=e,e>t){let e=X.current;e&&(e.scrollTop=e.scrollHeight)}},[w?.messages]);let ec=!w||0===w.messages.length,eu=k?.split("@")[0]??x??"",ed=eu?`${tf()}, ${eu}`:tf(),ef=(P?T.filter(e=>e.toLowerCase().includes(P.toLowerCase())):T).sort((e,t)=>e===A?-1:+(t===A)),eh=(0,t.jsxs)("div",{className:"w-[280px] h-[400px] flex flex-col overflow-hidden",children:[(0,t.jsx)("div",{className:"p-2 pb-1",children:(0,t.jsx)(u.Input,{autoFocus:!0,value:P,onChange:e=>_(e.target.value),placeholder:"Search models...",className:"h-8 text-[13px]"})}),(0,t.jsx)(c.ScrollArea,{className:"flex-1 h-0",children:ef.map(e=>{let n=e===A,r=th(e),{logo:i}=r?(0,tc.getProviderLogoAndName)(r):{logo:""};return(0,t.jsxs)(d.Button,{variant:"ghost",onClick:()=>er(e),className:`h-auto w-full justify-start gap-2 rounded px-3 py-[7px] font-normal ${n?"bg-accent":""}`,children:[i?(0,t.jsx)("img",{src:i,alt:"",className:"w-4 h-4 object-contain shrink-0",onError:e=>{e.currentTarget.style.display="none"}}):(0,t.jsx)("span",{className:"w-4 shrink-0"}),(0,t.jsx)("span",{className:"flex-1 text-left text-[13px] text-foreground overflow-hidden text-ellipsis whitespace-nowrap",children:e}),n&&(0,t.jsx)(l.Check,{className:"h-3.5 w-3.5 text-primary shrink-0"})]},e)})})]}),em=O?(0,t.jsx)(s.Skeleton,{className:"w-40 h-8"}):(0,t.jsxs)(a.Popover,{open:M,onOpenChange:e=>{R(e),e||_("")},children:[(0,t.jsx)(a.PopoverTrigger,{render:(0,t.jsxs)(d.Button,{variant:"outline",size:"sm",className:"max-w-[240px] justify-start gap-1.5 overflow-hidden",children:[A?(0,t.jsxs)(t.Fragment,{children:[(()=>{let e=th(A),{logo:n}=e?(0,tc.getProviderLogoAndName)(e):{logo:""};return n?(0,t.jsx)("img",{src:n,alt:"",className:"w-4 h-4 object-contain shrink-0",onError:e=>{e.currentTarget.style.display="none"}}):null})(),(0,t.jsx)("span",{className:"overflow-hidden text-ellipsis whitespace-nowrap",children:A})]}):(0,t.jsx)("span",{className:"text-muted-foreground",children:"Select model"}),(0,t.jsx)(i.ChevronDown,{className:"h-3 w-3 text-muted-foreground shrink-0"})]})}),(0,t.jsx)(a.PopoverContent,{align:"start",side:"top",className:"p-0 w-auto",children:eh})]}),ep=e=>(0,t.jsxs)("div",{className:"bg-background rounded-xl border shadow-[0_1px_6px_rgba(0,0,0,0.06)] overflow-hidden",children:[(0,t.jsx)("textarea",{ref:Q,value:U,onChange:e=>V(e.target.value),onKeyDown:ea,placeholder:e?"Send a message...":"How can I help you today?",className:"w-full border-none outline-none resize-none text-[15px] text-foreground bg-transparent font-[inherit] box-border",style:{minHeight:e?52:80,padding:e?"16px 20px 8px":"20px 20px 8px"}}),(0,t.jsxs)("div",{className:"flex items-center justify-between border-t",style:{padding:e?"4px 12px 10px":"8px 12px 12px"},children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 min-w-0",children:[em,(0,t.jsxs)(a.Popover,{open:K,onOpenChange:G,children:[(0,t.jsx)(a.PopoverTrigger,{render:(0,t.jsxs)(d.Button,{variant:"outline",size:"sm",className:"gap-1 px-2.5 text-muted-foreground",children:[(0,t.jsx)(r.Plus,{className:"h-3.5 w-3.5"}),b.length>0&&(0,t.jsx)("span",{className:"text-xs text-primary font-medium",children:b.length})]})}),(0,t.jsx)(a.PopoverContent,{side:"top",align:"start",className:"p-0 w-auto",children:(0,t.jsx)(to,{accessToken:g,selectedServers:b,onChange:v})})]})]}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[e&&b.length>0&&(0,t.jsxs)("span",{className:"text-xs text-muted-foreground max-w-[160px] overflow-hidden text-ellipsis whitespace-nowrap",children:[b.length," tool",b.length>1?"s":""," connected"]}),W?(0,t.jsx)(d.Button,{variant:"outline",size:"icon-sm",onClick:el,className:"rounded-full shrink-0",children:(0,t.jsx)("div",{className:"w-2.5 h-2.5 bg-foreground rounded-[2px]"})}):(0,t.jsx)(d.Button,{size:"sm",onClick:()=>ei(U),disabled:!U.trim()||O||!A,children:"Send"})]})]})]});return(0,t.jsxs)(t.Fragment,{children:[j&&!J&&(0,t.jsxs)("div",{className:"bg-amber-50 border-b border-amber-200 px-5 py-1.5 text-[13px] text-amber-800 flex justify-between items-center",children:[(0,t.jsx)("span",{children:"Chat history won't be saved in this browser session"}),(0,t.jsx)(d.Button,{variant:"ghost",size:"icon-xs",onClick:()=>Y(!0),className:"text-amber-800 hover:bg-amber-100 hover:text-amber-800",children:(0,t.jsx)(o.X,{className:"size-3.5"})})]}),(0,t.jsx)("div",{className:"flex-1 min-h-0 overflow-hidden flex flex-col bg-background",children:ec?(0,t.jsxs)("div",{className:"flex-1 flex flex-col items-center justify-center px-6 pb-20",children:[(0,t.jsx)("h1",{className:"m-0 mb-8 text-[28px] font-semibold text-foreground tracking-tight text-center",children:ed}),(0,t.jsxs)("p",{className:"-mt-4 mb-7 text-sm text-muted-foreground text-center max-w-[520px] leading-relaxed",children:["Chat with 100+ LLMs + MCP tools; authenticate once, use them here."," ",(0,t.jsx)(d.Button,{variant:"link",onClick:()=>e.push(p.CHAT_ROUTES.integrations),className:"h-auto p-0 text-sm font-medium",children:"Open Integrations ->"})]}),(0,t.jsx)("div",{className:"w-full max-w-[680px]",children:ep(!1)}),(0,t.jsx)("div",{className:"flex gap-2 mt-3.5 flex-wrap justify-center",children:tu.map(e=>(0,t.jsx)(d.Button,{variant:"outline",size:"sm",onClick:()=>V(e+": "),className:"rounded-full px-4 text-muted-foreground",children:e},e))})]}):(0,t.jsxs)("div",{className:"flex-1 min-h-0 flex flex-col mx-auto w-full px-6 relative",style:{maxWidth:760},children:[(0,t.jsx)("div",{ref:X,className:"flex-1 min-h-0 overflow-auto pt-6",style:{overflowAnchor:"none"},children:(0,t.jsx)(tn,{messages:w.messages,isStreaming:W,onEditMessage:eo})}),ee&&(0,t.jsx)(d.Button,{variant:"ghost",size:"icon",onClick:()=>{let e=X.current;e&&(e.scrollTo({top:e.scrollHeight,behavior:"smooth"}),null!==en.current&&(en.current=e.scrollHeight))},className:"absolute bottom-[100px] left-1/2 -translate-x-1/2 z-10 rounded-full border bg-background/75 text-muted-foreground shadow-sm backdrop-blur-md hover:bg-background/95 hover:text-muted-foreground","aria-label":"Scroll to bottom",children:(0,t.jsx)(i.ChevronDown,{className:"h-3 w-3"})}),(0,t.jsx)("div",{className:"py-3 pb-6",children:ep(!0)})]})})]})}],321443)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/06mj74u6dgxb8.js b/litellm/proxy/_experimental/out/_next/static/chunks/06mj74u6dgxb8.js deleted file mode 100644 index 5a9bcb55b81..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/06mj74u6dgxb8.js +++ /dev/null @@ -1,23 +0,0 @@ -(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,321443,e=>{"use strict";var t=e.i(843476),n=e.i(271645),r=e.i(107233),i=e.i(664659),o=e.i(643531),a=e.i(37727),l=e.i(981140),s=e.i(820783),c=e.i(30030),u=e.i(726330),d=e.i(303536),f=e.i(765491),p=e.i(610772),h=e.i(853660),m=e.i(774606),g=e.i(296626),x=e.i(248425),b=e.i(991918),v=e.i(369340),k=e.i(186312),y=e.i(985369),w="Popover",[j,C]=(0,c.createContextScope)(w,[h.createPopperScope]),S=(0,h.createPopperScope)(),[N,E]=j(w),R=e=>{let{__scopePopover:r,children:i,open:o,defaultOpen:a,onOpenChange:l,modal:s=!1}=e,c=S(r),u=n.useRef(null),[d,f]=n.useState(!1),[m,g]=(0,v.useControllableState)({prop:o,defaultProp:a??!1,onChange:l,caller:w});return(0,t.jsx)(h.Root,{...c,children:(0,t.jsx)(N,{scope:r,contentId:(0,p.useId)(),triggerRef:u,open:m,onOpenChange:g,onOpenToggle:n.useCallback(()=>g(e=>!e),[g]),hasCustomAnchor:d,onCustomAnchorAdd:n.useCallback(()=>f(!0),[]),onCustomAnchorRemove:n.useCallback(()=>f(!1),[]),modal:s,children:i})})};R.displayName=w;var A="PopoverAnchor",D=n.forwardRef((e,r)=>{let{__scopePopover:i,...o}=e,a=E(A,i),l=S(i),{onCustomAnchorAdd:s,onCustomAnchorRemove:c}=a;return n.useEffect(()=>(s(),()=>c()),[s,c]),(0,t.jsx)(h.Anchor,{...l,...o,ref:r})});D.displayName=A;var P="PopoverTrigger",T=n.forwardRef((e,n)=>{let{__scopePopover:r,...i}=e,o=E(P,r),a=S(r),c=(0,s.useComposedRefs)(n,o.triggerRef),u=(0,t.jsx)(x.Primitive.button,{type:"button","aria-haspopup":"dialog","aria-expanded":o.open,"aria-controls":o.open?o.contentId:void 0,"data-state":K(o.open),...i,ref:c,onClick:(0,l.composeEventHandlers)(e.onClick,o.onOpenToggle)});return o.hasCustomAnchor?u:(0,t.jsx)(h.Anchor,{asChild:!0,...a,children:u})});T.displayName=P;var L="PopoverPortal",[O,F]=j(L,{forceMount:void 0}),z=e=>{let{__scopePopover:n,forceMount:r,children:i,container:o}=e,a=E(L,n);return(0,t.jsx)(O,{scope:n,forceMount:r,children:(0,t.jsx)(g.Presence,{present:r||a.open,children:(0,t.jsx)(m.Portal,{asChild:!0,container:o,children:i})})})};z.displayName=L;var _="PopoverContent",M=n.forwardRef((e,n)=>{let r=F(_,e.__scopePopover),{forceMount:i=r.forceMount,...o}=e,a=E(_,e.__scopePopover);return(0,t.jsx)(g.Presence,{present:i||a.open,children:a.modal?(0,t.jsx)(H,{...o,ref:n}):(0,t.jsx)(B,{...o,ref:n})})});M.displayName=_;var I=(0,b.createSlot)("PopoverContent.RemoveScroll"),H=n.forwardRef((e,r)=>{let i=E(_,e.__scopePopover),o=n.useRef(null),a=(0,s.useComposedRefs)(r,o),c=n.useRef(!1);return n.useEffect(()=>{let e=o.current;if(e)return(0,k.hideOthers)(e)},[]),(0,t.jsx)(y.RemoveScroll,{as:I,allowPinchZoom:!0,children:(0,t.jsx)($,{...e,ref:a,trapFocus:i.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:(0,l.composeEventHandlers)(e.onCloseAutoFocus,e=>{e.preventDefault(),c.current||i.triggerRef.current?.focus()}),onPointerDownOutside:(0,l.composeEventHandlers)(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=0===t.button&&!0===t.ctrlKey;c.current=2===t.button||n},{checkForDefaultPrevented:!1}),onFocusOutside:(0,l.composeEventHandlers)(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1})})})}),B=n.forwardRef((e,r)=>{let i=E(_,e.__scopePopover),o=n.useRef(!1),a=n.useRef(!1);return(0,t.jsx)($,{...e,ref:r,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(o.current||i.triggerRef.current?.focus(),t.preventDefault()),o.current=!1,a.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(o.current=!0,"pointerdown"===t.detail.originalEvent.type&&(a.current=!0));let n=t.target;i.triggerRef.current?.contains(n)&&t.preventDefault(),"focusin"===t.detail.originalEvent.type&&a.current&&t.preventDefault()}})}),$=n.forwardRef((e,n)=>{let{__scopePopover:r,trapFocus:i,onOpenAutoFocus:o,onCloseAutoFocus:a,disableOutsidePointerEvents:l,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:p,onInteractOutside:m,...g}=e,x=E(_,r),b=S(r);return(0,d.useFocusGuards)(),(0,t.jsx)(f.FocusScope,{asChild:!0,loop:!0,trapped:i,onMountAutoFocus:o,onUnmountAutoFocus:a,children:(0,t.jsx)(u.DismissableLayer,{asChild:!0,disableOutsidePointerEvents:l,onInteractOutside:m,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:p,onDismiss:()=>x.onOpenChange(!1),deferPointerDownOutside:!0,children:(0,t.jsx)(h.Content,{"data-state":K(x.open),role:"dialog",id:x.contentId,...b,...g,ref:n,style:{...g.style,"--radix-popover-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-popover-content-available-width":"var(--radix-popper-available-width)","--radix-popover-content-available-height":"var(--radix-popper-available-height)","--radix-popover-trigger-width":"var(--radix-popper-anchor-width)","--radix-popover-trigger-height":"var(--radix-popper-anchor-height)"}})})})}),W="PopoverClose",q=n.forwardRef((e,n)=>{let{__scopePopover:r,...i}=e,o=E(W,r);return(0,t.jsx)(x.Primitive.button,{type:"button",...i,ref:n,onClick:(0,l.composeEventHandlers)(e.onClick,()=>o.onOpenChange(!1))})});q.displayName=W;var U=n.forwardRef((e,n)=>{let{__scopePopover:r,...i}=e,o=S(r);return(0,t.jsx)(h.Arrow,{...o,...i,ref:n})});function K(e){return e?"open":"closed"}U.displayName="PopoverArrow",e.s(["Anchor",0,D,"Arrow",0,U,"Close",0,q,"Content",0,M,"Popover",0,R,"PopoverAnchor",0,D,"PopoverArrow",0,U,"PopoverClose",0,q,"PopoverContent",0,M,"PopoverPortal",0,z,"PopoverTrigger",0,T,"Portal",0,z,"Root",0,R,"Trigger",0,T,"createPopoverScope",0,C],178761);var V=e.i(178761),V=V,G=e.i(115504);function Z({...e}){return(0,t.jsx)(V.Root,{"data-slot":"popover",...e})}function J({...e}){return(0,t.jsx)(V.Trigger,{"data-slot":"popover-trigger",...e})}function X({className:e,align:n="center",sideOffset:r=4,...i}){return(0,t.jsx)(V.Portal,{children:(0,t.jsx)(V.Content,{"data-slot":"popover-content",align:n,sideOffset:r,className:(0,G.cn)("z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",e),...i})})}var Y=e.i(302747),Q=e.i(759684),ee=e.i(793479),et=e.i(519455),en=e.i(888259),er=e.i(618566),ei=e.i(405033),eo=e.i(360179),ea=e.i(195116),el=e.i(174886),es=e.i(788699),ec=e.i(746798),eu=e.i(934620),ed="Collapsible",[ef,ep]=(0,c.createContextScope)(ed),[eh,em]=ef(ed),eg=n.forwardRef((e,r)=>{let{__scopeCollapsible:i,open:o,defaultOpen:a,disabled:l,onOpenChange:s,...c}=e,[u,d]=(0,v.useControllableState)({prop:o,defaultProp:a??!1,onChange:s,caller:ed});return(0,t.jsx)(eh,{scope:i,disabled:l,contentId:(0,p.useId)(),open:u,onOpenToggle:n.useCallback(()=>d(e=>!e),[d]),children:(0,t.jsx)(x.Primitive.div,{"data-state":ew(u),"data-disabled":l?"":void 0,...c,ref:r})})});eg.displayName=ed;var ex="CollapsibleTrigger",eb=n.forwardRef((e,n)=>{let{__scopeCollapsible:r,...i}=e,o=em(ex,r);return(0,t.jsx)(x.Primitive.button,{type:"button","aria-controls":o.open?o.contentId:void 0,"aria-expanded":o.open||!1,"data-state":ew(o.open),"data-disabled":o.disabled?"":void 0,disabled:o.disabled,...i,ref:n,onClick:(0,l.composeEventHandlers)(e.onClick,o.onOpenToggle)})});eb.displayName=ex;var ev="CollapsibleContent",ek=n.forwardRef((e,n)=>{let{forceMount:r,...i}=e,o=em(ev,e.__scopeCollapsible);return(0,t.jsx)(g.Presence,{present:r||o.open,children:({present:e})=>(0,t.jsx)(ey,{...i,ref:n,present:e})})});ek.displayName=ev;var ey=n.forwardRef((e,r)=>{let{__scopeCollapsible:i,present:o,children:a,...l}=e,c=em(ev,i),[u,d]=n.useState(o),f=n.useRef(null),p=(0,s.useComposedRefs)(r,f),h=n.useRef(0),m=h.current,g=n.useRef(0),b=g.current,v=c.open||u,k=n.useRef(v),y=n.useRef(void 0);return n.useEffect(()=>{let e=requestAnimationFrame(()=>k.current=!1);return()=>cancelAnimationFrame(e)},[]),(0,eu.useLayoutEffect)(()=>{let e=f.current;if(e){y.current=y.current||{transitionDuration:e.style.transitionDuration,animationName:e.style.animationName},e.style.transitionDuration="0s",e.style.animationName="none";let t=e.getBoundingClientRect();h.current=t.height,g.current=t.width,k.current||(e.style.transitionDuration=y.current.transitionDuration,e.style.animationName=y.current.animationName),d(o)}},[c.open,o]),(0,t.jsx)(x.Primitive.div,{"data-state":ew(c.open),"data-disabled":c.disabled?"":void 0,id:c.contentId,hidden:!v,...l,ref:p,style:{"--radix-collapsible-content-height":m?`${m}px`:void 0,"--radix-collapsible-content-width":b?`${b}px`:void 0,...e.style},children:v&&a})});function ew(e){return e?"open":"closed"}e.s(["Collapsible",0,eg,"CollapsibleContent",0,ek,"CollapsibleTrigger",0,eb,"Content",0,ek,"Root",0,eg,"Trigger",0,eb,"createCollapsibleScope",0,ep],687607);var ej=e.i(687607),ej=ej;function eC({...e}){return(0,t.jsx)(ej.Root,{"data-slot":"collapsible",...e})}function eS({...e}){return(0,t.jsx)(ej.CollapsibleTrigger,{"data-slot":"collapsible-trigger",...e})}function eN({...e}){return(0,t.jsx)(ej.CollapsibleContent,{"data-slot":"collapsible-content",...e})}var eE=e.i(918789);function eR(e,t){let n=String(e);if("string"!=typeof t)throw TypeError("Expected character");let r=0,i=n.indexOf(t);for(;-1!==i;)r++,i=n.indexOf(t,i+t.length);return r}var eA=e.i(420061),eD=e.i(997803),eP=e.i(733644),eT=e.i(457579);let eL="phrasing",eO=["autolink","link","image","label"];function eF(e){this.enter({type:"link",title:null,url:"",children:[]},e)}function ez(e){this.config.enter.autolinkProtocol.call(this,e)}function e_(e){this.config.exit.autolinkProtocol.call(this,e)}function eM(e){this.config.exit.data.call(this,e);let t=this.stack[this.stack.length-1];(0,eA.ok)("link"===t.type),t.url="http://"+this.sliceSerialize(e)}function eI(e){this.config.exit.autolinkEmail.call(this,e)}function eH(e){this.exit(e)}function eB(e){!function(e,t,n){let r=(0,eT.convert)((n||{}).ignore||[]),i=function(e){let t=[];if(!Array.isArray(e))throw TypeError("Expected find and replace tuple or list of tuples");let n=!e[0]||Array.isArray(e[0])?e:[e],r=-1;for(;++r0?{type:"text",value:o}:void 0),!1===o?r.lastIndex=n+1:(l!==n&&u.push({type:"text",value:e.value.slice(l,n)}),Array.isArray(o)?u.push(...o):o&&u.push(o),l=n+d[0].length,c=!0),!r.global)break;d=r.exec(e.value)}return c?(l?\]}]+$/.exec(e);if(!t)return[e,void 0];e=e.slice(0,t.index);let n=t[0],r=n.indexOf(")"),i=eR(e,"("),o=eR(e,")");for(;-1!==r&&i>o;)e+=n.slice(0,r+1),r=(n=n.slice(r+1)).indexOf(")"),o++;return[e,n]}(n+r);if(!l[0])return!1;let s={type:"link",title:null,url:a+t+l[0],children:[{type:"text",value:t+l[0]}]};return l[1]?[s,{type:"text",value:l[1]}]:s}function eW(e,t,n,r){return!(!eq(r,!0)||/[-\d_]$/.test(n))&&{type:"link",title:null,url:"mailto:"+t+"@"+n,children:[{type:"text",value:t+"@"+n}]}}function eq(e,t){let n=e.input.charCodeAt(e.index-1);return(0===e.index||(0,eD.unicodeWhitespace)(n)||(0,eD.unicodePunctuation)(n))&&(!t||47!==n)}var eU=e.i(431745);function eK(){this.buffer()}function eV(e){this.enter({type:"footnoteReference",identifier:"",label:""},e)}function eG(){this.buffer()}function eZ(e){this.enter({type:"footnoteDefinition",identifier:"",label:"",children:[]},e)}function eJ(e){let t=this.resume(),n=this.stack[this.stack.length-1];(0,eA.ok)("footnoteReference"===n.type),n.identifier=(0,eU.normalizeIdentifier)(this.sliceSerialize(e)).toLowerCase(),n.label=t}function eX(e){this.exit(e)}function eY(e){let t=this.resume(),n=this.stack[this.stack.length-1];(0,eA.ok)("footnoteDefinition"===n.type),n.identifier=(0,eU.normalizeIdentifier)(this.sliceSerialize(e)).toLowerCase(),n.label=t}function eQ(e){this.exit(e)}function e0(e,t,n,r){let i=n.createTracker(r),o=i.move("[^"),a=n.enter("footnoteReference"),l=n.enter("reference");return o+=i.move(n.safe(n.associationId(e),{after:"]",before:o})),l(),a(),o+=i.move("]")}function e1(e,t,n){return 0===t?e:e2(e,t,n)}function e2(e,t,n){return(n?"":" ")+e}e0.peek=function(){return"["};let e3=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];function e4(e){this.enter({type:"delete",children:[]},e)}function e5(e){this.exit(e)}function e9(e,t,n,r){let i=n.createTracker(r),o=n.enter("strikethrough"),a=i.move("~~");return a+=n.containerPhrasing(e,{...i.current(),before:a,after:"~"}),a+=i.move("~~"),o(),a}function e6(e){return e.length}function e8(e){let t="string"==typeof e?e.codePointAt(0):0;return 67===t||99===t?99:76===t||108===t?108:114*(82===t||114===t)}e9.peek=function(){return"~"};var e7=e.i(682523);e.i(784801);e.i(900065);function te(e,t,n){let r=e.value||"",i="`",o=-1;for(;RegExp("(^|[^`])"+i+"([^`]|$)").test(r);)i+="`";for(/[^ \r\n]/.test(r)&&(/^[ \r\n]/.test(r)&&/[ \r\n]$/.test(r)||/^`|`$/.test(r))&&(r=" "+r+" ");++o-1?t.start:1)+(!1===n.options.incrementListMarker?0:t.children.indexOf(e))+o);let a=o.length+1;("tab"===i||"mixed"===i&&(t&&"list"===t.type&&t.spread||e.spread))&&(a=4*Math.ceil(a/4));let l=n.createTracker(r);l.move(o+" ".repeat(a-o.length)),l.shift(a);let s=n.enter("listItem"),c=n.indentLines(n.containerFlow(e,l.current()),function(e,t,n){return t?(n?"":" ".repeat(a))+e:(n?o:o+" ".repeat(a-o.length))+e});return s(),c};function tn(e){let t=e._align;(0,eA.ok)(t,"expected `_align` on table"),this.enter({type:"table",align:t.map(function(e){return"none"===e?null:e}),children:[]},e),this.data.inTable=!0}function tr(e){this.exit(e),this.data.inTable=void 0}function ti(e){this.enter({type:"tableRow",children:[]},e)}function to(e){this.exit(e)}function ta(e){this.enter({type:"tableCell",children:[]},e)}function tl(e){let t=this.resume();this.data.inTable&&(t=t.replace(/\\([\\|])/g,ts));let n=this.stack[this.stack.length-1];(0,eA.ok)("inlineCode"===n.type),n.value=t,this.exit(e)}function ts(e,t){return"|"===t?t:e}function tc(e){let t=this.stack[this.stack.length-2];(0,eA.ok)("listItem"===t.type),t.checked="taskListCheckValueChecked"===e.type}function tu(e){let t=this.stack[this.stack.length-2];if(t&&"listItem"===t.type&&"boolean"==typeof t.checked){let e=this.stack[this.stack.length-1];(0,eA.ok)("paragraph"===e.type);let n=e.children[0];if(n&&"text"===n.type){let r,i=t.children,o=-1;for(;++o0&&!n&&(e[e.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}ty[43]=tk,ty[45]=tk,ty[46]=tk,ty[95]=tk,ty[72]=[tk,tv],ty[104]=[tk,tv],ty[87]=[tk,tb],ty[119]=[tk,tb];var tR=e.i(653161),tA=e.i(204108);let tD={tokenize:function(e,t,n){let r=this;return(0,tA.factorySpace)(e,function(e){let i=r.events[r.events.length-1];return i&&"gfmFootnoteDefinitionIndent"===i[1].type&&4===i[2].sliceSerialize(i[1],!0).length?t(e):n(e)},"gfmFootnoteDefinitionIndent",5)},partial:!0};function tP(e,t,n){let r,i=this,o=i.events.length,a=i.parser.gfmFootnotes||(i.parser.gfmFootnotes=[]);for(;o--;){let e=i.events[o][1];if("labelImage"===e.type){r=e;break}if("gfmFootnoteCall"===e.type||"labelLink"===e.type||"label"===e.type||"image"===e.type||"link"===e.type)break}return function(o){if(!r||!r._balanced)return n(o);let l=(0,eU.normalizeIdentifier)(i.sliceSerialize({start:r.end,end:i.now()}));return 94===l.codePointAt(0)&&a.includes(l.slice(1))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(o),e.exit("gfmFootnoteCallLabelMarker"),t(o)):n(o)}}function tT(e,t){let n=e.length;for(;n--;)if("labelImage"===e[n][1].type&&"enter"===e[n][0]){e[n][1];break}e[n+1][1].type="data",e[n+3][1].type="gfmFootnoteCallLabelMarker";let r={type:"gfmFootnoteCall",start:Object.assign({},e[n+3][1].start),end:Object.assign({},e[e.length-1][1].end)},i={type:"gfmFootnoteCallMarker",start:Object.assign({},e[n+3][1].end),end:Object.assign({},e[n+3][1].end)};i.end.column++,i.end.offset++,i.end._bufferIndex++;let o={type:"gfmFootnoteCallString",start:Object.assign({},i.end),end:Object.assign({},e[e.length-1][1].start)},a={type:"chunkString",contentType:"string",start:Object.assign({},o.start),end:Object.assign({},o.end)},l=[e[n+1],e[n+2],["enter",r,t],e[n+3],e[n+4],["enter",i,t],["exit",i,t],["enter",o,t],["enter",a,t],["exit",a,t],["exit",o,t],e[e.length-2],e[e.length-1],["exit",r,t]];return e.splice(n,e.length-n+1,...l),e}function tL(e,t,n){let r,i=this,o=i.parser.gfmFootnotes||(i.parser.gfmFootnotes=[]),a=0;return function(t){return e.enter("gfmFootnoteCall"),e.enter("gfmFootnoteCallLabelMarker"),e.consume(t),e.exit("gfmFootnoteCallLabelMarker"),l};function l(t){return 94!==t?n(t):(e.enter("gfmFootnoteCallMarker"),e.consume(t),e.exit("gfmFootnoteCallMarker"),e.enter("gfmFootnoteCallString"),e.enter("chunkString").contentType="string",s)}function s(l){if(a>999||93===l&&!r||null===l||91===l||(0,eD.markdownLineEndingOrSpace)(l))return n(l);if(93===l){e.exit("chunkString");let r=e.exit("gfmFootnoteCallString");return o.includes((0,eU.normalizeIdentifier)(i.sliceSerialize(r)))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(l),e.exit("gfmFootnoteCallLabelMarker"),e.exit("gfmFootnoteCall"),t):n(l)}return(0,eD.markdownLineEndingOrSpace)(l)||(r=!0),a++,e.consume(l),92===l?c:s}function c(t){return 91===t||92===t||93===t?(e.consume(t),a++,s):s(t)}}function tO(e,t,n){let r,i,o=this,a=o.parser.gfmFootnotes||(o.parser.gfmFootnotes=[]),l=0;return function(t){return e.enter("gfmFootnoteDefinition")._container=!0,e.enter("gfmFootnoteDefinitionLabel"),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(t),e.exit("gfmFootnoteDefinitionLabelMarker"),s};function s(t){return 94===t?(e.enter("gfmFootnoteDefinitionMarker"),e.consume(t),e.exit("gfmFootnoteDefinitionMarker"),e.enter("gfmFootnoteDefinitionLabelString"),e.enter("chunkString").contentType="string",c):n(t)}function c(t){if(l>999||93===t&&!i||null===t||91===t||(0,eD.markdownLineEndingOrSpace)(t))return n(t);if(93===t){e.exit("chunkString");let n=e.exit("gfmFootnoteDefinitionLabelString");return r=(0,eU.normalizeIdentifier)(o.sliceSerialize(n)),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(t),e.exit("gfmFootnoteDefinitionLabelMarker"),e.exit("gfmFootnoteDefinitionLabel"),d}return(0,eD.markdownLineEndingOrSpace)(t)||(i=!0),l++,e.consume(t),92===t?u:c}function u(t){return 91===t||92===t||93===t?(e.consume(t),l++,c):c(t)}function d(t){return 58===t?(e.enter("definitionMarker"),e.consume(t),e.exit("definitionMarker"),a.includes(r)||a.push(r),(0,tA.factorySpace)(e,f,"gfmFootnoteDefinitionWhitespace")):n(t)}function f(e){return t(e)}}function tF(e,t,n){return e.check(tR.blankLine,t,e.attempt(tD,t,n))}function tz(e){e.exit("gfmFootnoteDefinition")}var t_=e.i(938402),tM=e.i(810291);class tI{constructor(){this.map=[]}add(e,t,n){!function(e,t,n,r){let i=0;if(0!==n||0!==r.length){for(;i0;)t-=1,n.push(e.slice(this.map[t][0]+this.map[t][1]),this.map[t][2]),e.length=this.map[t][0];n.push(e.slice()),e.length=0;let r=n.pop();for(;r;){for(let t of r)e.push(t);r=n.pop()}this.map.length=0}}function tH(e,t,n){let r,i=this,o=0,a=0;return function(e){let t=i.events.length-1;for(;t>-1;){let e=i.events[t][1].type;if("lineEnding"===e||"linePrefix"===e)t--;else break}let r=t>-1?i.events[t][1].type:null,o="tableHead"===r||"tableRow"===r?b:l;return o===b&&i.parser.lazy[i.now().line]?n(e):o(e)};function l(t){var n;return e.enter("tableHead"),e.enter("tableRow"),124===(n=t)||(r=!0,a+=1),s(n)}function s(t){return null===t?n(t):(0,eD.markdownLineEnding)(t)?a>1?(a=0,i.interrupt=!0,e.exit("tableRow"),e.enter("lineEnding"),e.consume(t),e.exit("lineEnding"),d):n(t):(0,eD.markdownSpace)(t)?(0,tA.factorySpace)(e,s,"whitespace")(t):(a+=1,r&&(r=!1,o+=1),124===t)?(e.enter("tableCellDivider"),e.consume(t),e.exit("tableCellDivider"),r=!0,s):(e.enter("data"),c(t))}function c(t){return null===t||124===t||(0,eD.markdownLineEndingOrSpace)(t)?(e.exit("data"),s(t)):(e.consume(t),92===t?u:c)}function u(t){return 92===t||124===t?(e.consume(t),c):c(t)}function d(t){return(i.interrupt=!1,i.parser.lazy[i.now().line])?n(t):(e.enter("tableDelimiterRow"),r=!1,(0,eD.markdownSpace)(t))?(0,tA.factorySpace)(e,f,"linePrefix",i.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(t):f(t)}function f(t){return 45===t||58===t?h(t):124===t?(r=!0,e.enter("tableCellDivider"),e.consume(t),e.exit("tableCellDivider"),p):n(t)}function p(t){return(0,eD.markdownSpace)(t)?(0,tA.factorySpace)(e,h,"whitespace")(t):h(t)}function h(t){return 58===t?(a+=1,r=!0,e.enter("tableDelimiterMarker"),e.consume(t),e.exit("tableDelimiterMarker"),m):45===t?(a+=1,m(t)):null===t||(0,eD.markdownLineEnding)(t)?x(t):n(t)}function m(t){return 45===t?(e.enter("tableDelimiterFiller"),function t(n){return 45===n?(e.consume(n),t):58===n?(r=!0,e.exit("tableDelimiterFiller"),e.enter("tableDelimiterMarker"),e.consume(n),e.exit("tableDelimiterMarker"),g):(e.exit("tableDelimiterFiller"),g(n))}(t)):n(t)}function g(t){return(0,eD.markdownSpace)(t)?(0,tA.factorySpace)(e,x,"whitespace")(t):x(t)}function x(i){if(124===i)return f(i);if(null===i||(0,eD.markdownLineEnding)(i))return r&&o===a?(e.exit("tableDelimiterRow"),e.exit("tableHead"),t(i)):n(i);return n(i)}function b(t){return e.enter("tableRow"),v(t)}function v(n){return 124===n?(e.enter("tableCellDivider"),e.consume(n),e.exit("tableCellDivider"),v):null===n||(0,eD.markdownLineEnding)(n)?(e.exit("tableRow"),t(n)):(0,eD.markdownSpace)(n)?(0,tA.factorySpace)(e,v,"whitespace")(n):(e.enter("data"),k(n))}function k(t){return null===t||124===t||(0,eD.markdownLineEndingOrSpace)(t)?(e.exit("data"),v(t)):(e.consume(t),92===t?y:k)}function y(t){return 92===t||124===t?(e.consume(t),k):k(t)}}function tB(e,t){let n,r,i,o=-1,a=!0,l=0,s=[0,0,0,0],c=[0,0,0,0],u=!1,d=0,f=new tI;for(;++on[2]+1){let t=n[2]+1,r=n[3]-n[2]-1;e.add(t,r,[])}}e.add(n[3]+1,0,[["exit",a,t]])}return void 0!==i&&(o.end=Object.assign({},tq(t.events,i)),e.add(i,0,[["exit",o,t]]),o=void 0),o}function tW(e,t,n,r,i){let o=[],a=tq(t.events,n);i&&(i.end=Object.assign({},a),o.push(["exit",i,t])),r.end=Object.assign({},a),o.push(["exit",r,t]),e.add(n+1,0,o)}function tq(e,t){let n=e[t],r="enter"===n[0]?"start":"end";return n[1][r]}let tU={name:"tasklistCheck",tokenize:function(e,t,n){let r=this;return function(t){return null===r.previous&&r._gfmTasklistFirstContentOfListItem?(e.enter("taskListCheck"),e.enter("taskListCheckMarker"),e.consume(t),e.exit("taskListCheckMarker"),i):n(t)};function i(t){return(0,eD.markdownLineEndingOrSpace)(t)?(e.enter("taskListCheckValueUnchecked"),e.consume(t),e.exit("taskListCheckValueUnchecked"),o):88===t||120===t?(e.enter("taskListCheckValueChecked"),e.consume(t),e.exit("taskListCheckValueChecked"),o):n(t)}function o(t){return 93===t?(e.enter("taskListCheckMarker"),e.consume(t),e.exit("taskListCheckMarker"),e.exit("taskListCheck"),a):n(t)}function a(r){return(0,eD.markdownLineEnding)(r)?t(r):(0,eD.markdownSpace)(r)?e.check({tokenize:tK},t,n)(r):n(r)}}};function tK(e,t,n){return(0,tA.factorySpace)(e,function(e){return null===e?n(e):t(e)},"whitespace")}let tV={};function tG(e){var t;let n,r,i,o=e||tV,a=this.data(),l=a.micromarkExtensions||(a.micromarkExtensions=[]),s=a.fromMarkdownExtensions||(a.fromMarkdownExtensions=[]),c=a.toMarkdownExtensions||(a.toMarkdownExtensions=[]);l.push((t=o,(0,tf.combineExtensions)([{text:ty},{document:{91:{name:"gfmFootnoteDefinition",tokenize:tO,continuation:{tokenize:tF},exit:tz}},text:{91:{name:"gfmFootnoteCall",tokenize:tL},93:{name:"gfmPotentialFootnoteCall",add:"after",tokenize:tP,resolveTo:tT}}},(n=(t||{}).singleTilde,r={name:"strikethrough",tokenize:function(e,t,r){let i=this.previous,o=this.events,a=0;return function(l){return 126===i&&"characterEscape"!==o[o.length-1][1].type?r(l):(e.enter("strikethroughSequenceTemporary"),function o(l){let s=(0,e7.classifyCharacter)(i);if(126===l)return a>1?r(l):(e.consume(l),a++,o);if(a<2&&!n)return r(l);let c=e.exit("strikethroughSequenceTemporary"),u=(0,e7.classifyCharacter)(l);return c._open=!u||2===u&&!!s,c._close=!s||2===s&&!!u,t(l)}(l))}},resolveAll:function(e,t){let n=-1;for(;++n0&&(o.shift(4),a+=o.move((i?"\n":" ")+n.indentLines(n.containerFlow(e,o.current()),i?e2:e1))),l(),a},footnoteReference:e0},unsafe:[{character:"[",inConstruct:["label","phrasing","reference"]}]}),{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:e3}],handlers:{delete:e9}},function(e){let t=e||{},n=t.tableCellPadding,r=t.tablePipeAlign,i=t.stringLength,o=n?" ":"|";return{unsafe:[{character:"\r",inConstruct:"tableCell"},{character:"\n",inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{inlineCode:function(e,t,n){let r=te(e,t,n);return n.stack.includes("tableCell")&&(r=r.replace(/\|/g,"\\$&")),r},table:function(e,t,n,r){return l(function(e,t,n){let r=e.children,i=-1,o=[],a=t.enter("table");for(;++ic&&(c=e[u].length);++os[o])&&(s[o]=e)}t.push(a)}a[u]=t,l[u]=r}let f=-1;if("object"==typeof r&&"length"in r)for(;++fs[f]&&(s[f]=i),h[f]=i),p[f]=a}a.splice(1,0,p),l.splice(1,0,h),u=-1;let m=[];for(;++u{l&&d.current&&(d.current.focus(),d.current.selectionStart=d.current.value.length)},[l]),(0,n.useEffect)(()=>{let e=d.current;e&&(e.style.height="auto",e.style.height=`${e.scrollHeight}px`)},[c,l]);let f=()=>{let t=c.trim();t&&t!==e.content&&r&&r(e.id,t),s(!1)};return l?(0,t.jsx)("div",{className:"flex flex-col items-end",children:(0,t.jsxs)("div",{className:"w-[72%] bg-background border-2 border-primary rounded-xl overflow-hidden shadow-[0_0_0_3px_rgba(var(--primary)/0.1)]",children:[(0,t.jsx)("textarea",{ref:d,value:c,onChange:e=>u(e.target.value),onKeyDown:t=>{"Enter"!==t.key||t.shiftKey||(t.preventDefault(),f()),"Escape"===t.key&&(u(e.content),s(!1))},className:"w-full px-3.5 py-2.5 border-none outline-none resize-none text-sm leading-relaxed text-foreground font-[inherit] bg-transparent box-border min-h-[40px]"}),(0,t.jsxs)("div",{className:"flex justify-end gap-2 px-2.5 py-1.5 border-t",children:[(0,t.jsx)(et.Button,{variant:"outline",size:"sm",onClick:()=>{u(e.content),s(!1)},children:"Cancel"}),(0,t.jsx)(et.Button,{size:"sm",onClick:f,disabled:!c.trim(),children:"Save & Send"})]})]})}):(0,t.jsxs)("div",{className:"flex flex-col items-end w-full",onMouseEnter:()=>a(!0),onMouseLeave:()=>a(!1),children:[(0,t.jsxs)("div",{className:"flex items-end gap-1.5 max-w-[72%]",children:[o&&!i&&r&&(0,t.jsx)(ec.TooltipProvider,{delayDuration:300,children:(0,t.jsxs)(ec.Tooltip,{children:[(0,t.jsx)(ec.TooltipTrigger,{asChild:!0,children:(0,t.jsx)(et.Button,{variant:"ghost",size:"icon-xs",onClick:()=>{u(e.content),s(!0)},className:"text-muted-foreground hover:text-foreground shrink-0",children:(0,t.jsx)(es.Pencil,{className:"size-3.5"})})}),(0,t.jsx)(ec.TooltipContent,{children:(0,t.jsx)("p",{children:"Edit message"})})]})}),(0,t.jsx)("div",{className:"bg-muted rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed whitespace-pre-wrap break-words text-foreground",children:e.content})]}),(0,t.jsx)("span",{className:"text-[11px] text-muted-foreground mt-1",children:t0(e.timestamp)})]})}function t3({message:e,isLastMessage:r,isStreaming:i,isTypingIndicator:o,mcpEvents:a}){let[l,s]=(0,n.useState)(0),c=(0,n.useRef)(i);(0,n.useEffect)(()=>{c.current&&!i&&s(e=>e+1),c.current=i},[i]);let u=r&&i&&!e.reasoningContent,d=!!e.reasoningContent||u;if(o)return(0,t.jsx)("div",{className:"flex flex-col items-start",children:(0,t.jsx)("div",{className:"flex items-center gap-1 px-1 py-2.5",children:(0,t.jsx)(t9,{})})});let f=e.content,p=!1;return f.endsWith("[stopped]")&&(f=f.slice(0,-9),p=!0),(0,t.jsxs)("div",{className:"flex flex-col items-start max-w-[80%]",children:[d&&(u?(0,t.jsx)(t5,{}):(0,t.jsx)(tX.default,{reasoningContent:e.reasoningContent},l)),(0,t.jsxs)("div",{className:"text-sm leading-[1.7] text-foreground break-words",children:[(0,t.jsx)(eE.default,{remarkPlugins:[tG],components:{code:t1},children:f}),p&&(0,t.jsx)("span",{className:"text-muted-foreground italic",children:" [stopped]"})]}),(0,t.jsx)(t4,{text:f}),a&&a.length>0&&(0,t.jsx)("div",{className:"mt-2 max-w-full",children:(0,t.jsx)(tY.default,{events:a})})]})}function t4({text:e}){let[r,i]=(0,n.useState)(!1);return(0,t.jsx)("div",{className:"flex items-center gap-1 mt-1.5",children:(0,t.jsx)(ec.TooltipProvider,{delayDuration:300,children:(0,t.jsxs)(ec.Tooltip,{children:[(0,t.jsx)(ec.TooltipTrigger,{asChild:!0,children:(0,t.jsx)(et.Button,{variant:"ghost",size:"icon-xs",onClick:()=>{navigator.clipboard.writeText(e).then(()=>{i(!0),setTimeout(()=>i(!1),2e3)}).catch(()=>{})},className:r?"text-emerald-600":"text-muted-foreground hover:text-foreground",children:r?(0,t.jsx)(o.Check,{className:"size-3.5"}):(0,t.jsx)(el.Copy,{className:"size-3.5"})})}),(0,t.jsx)(ec.TooltipContent,{children:(0,t.jsx)("p",{children:r?"Copied!":"Copy"})})]})})})}function t5(){return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("style",{children:` - @keyframes thinking-pulse { - 0%, 100% { opacity: 0.4; } - 50% { opacity: 1; } - } - .chat-thinking-text { - animation: thinking-pulse 1.4s ease-in-out infinite; - } - `}),(0,t.jsx)("div",{className:"inline-flex items-center gap-1.5 px-2.5 mb-2 bg-muted/50 border rounded-lg text-xs text-muted-foreground",children:(0,t.jsx)("span",{className:"chat-thinking-text py-1",children:"Thinking..."})})]})}function t9(){return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("style",{children:` - @keyframes chat-typing-bounce { - 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } - 30% { transform: translateY(-4px); opacity: 1; } - } - .chat-dot { - width: 7px; - height: 7px; - border-radius: 50%; - background-color: var(--color-muted-foreground); - animation: chat-typing-bounce 1.2s ease-in-out infinite; - } - .chat-dot:nth-child(2) { animation-delay: 0.2s; } - .chat-dot:nth-child(3) { animation-delay: 0.4s; } - `}),(0,t.jsx)("div",{className:"chat-dot"}),(0,t.jsx)("div",{className:"chat-dot"}),(0,t.jsx)("div",{className:"chat-dot"})]})}function t6({message:e}){let r=e.toolArgs?function e(t){let n={};for(let[r,i]of Object.entries(t))tQ.test(r)?n[r]="[redacted]":Array.isArray(i)?n[r]=i.map(t=>null===t||"object"!=typeof t||Array.isArray(t)?t:e(t)):null!==i&&"object"==typeof i?n[r]=e(i):n[r]=i;return n}(e.toolArgs):void 0,[i,o]=(0,n.useState)(!1);return(0,t.jsxs)("div",{className:"max-w-[80%]",children:[(0,t.jsxs)(eC,{open:i,onOpenChange:o,children:[(0,t.jsxs)(eS,{className:"flex items-center gap-1.5 text-[13px] px-3 py-2 border rounded-lg bg-muted/50 hover:bg-muted transition-colors w-full text-left",children:[(0,t.jsx)(ea.Wrench,{className:"h-3.5 w-3.5 text-muted-foreground"}),(0,t.jsx)("span",{className:"font-medium text-foreground",children:e.toolName??"Tool call"})]}),(0,t.jsxs)(eN,{className:"border border-t-0 rounded-b-lg px-3 py-2 bg-muted/30",children:[void 0!==r&&(0,t.jsxs)("div",{className:e.toolResult?"mb-3":"",children:[(0,t.jsx)("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-1",children:"Arguments"}),(0,t.jsx)("pre",{className:"m-0 p-2 bg-muted rounded-md text-xs font-mono whitespace-pre-wrap break-words text-foreground",children:JSON.stringify(r,null,2)})]}),e.toolResult&&(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-1",children:"Result"}),(0,t.jsx)("div",{className:"text-[13px] text-foreground whitespace-pre-wrap break-words font-mono",children:e.toolResult})]})]})]}),(0,t.jsx)("div",{className:"text-[11px] text-muted-foreground mt-1",children:t0(e.timestamp)})]})}let t8=({messages:e,isStreaming:n,onEditMessage:r})=>{let i=e.length-1,o=e[i]??null,a=n&&null!==o&&"assistant"===o.role&&""===o.content;return(0,t.jsx)("div",{className:"flex flex-col gap-4",children:e.map((e,o)=>{let l=o===i;return"user"===e.role?(0,t.jsx)(t2,{message:e,onEdit:r,isStreaming:n},e.id):"tool"===e.role?(0,t.jsx)(t6,{message:e},e.id):(0,t.jsx)(t3,{message:e,isLastMessage:l,isStreaming:n,isTypingIndicator:l&&a,mcpEvents:e.mcpEvents},e.id)})})};var t7=e.i(531278),ne=e.i(635804),nt="Switch",[nn,nr]=(0,c.createContextScope)(nt),[ni,no]=nn(nt);function na(e){let{__scopeSwitch:r,checked:i,children:o,defaultChecked:a,disabled:l,form:s,name:c,onCheckedChange:u,required:d,value:f="on",internal_do_not_use_render:p}=e,[h,m]=(0,v.useControllableState)({prop:i,defaultProp:a??!1,onChange:u,caller:nt}),[g,x]=n.useState(null),[b,k]=n.useState(null),y=n.useRef(!1),w=!g||!!s||!!g.closest("form"),j={checked:h,setChecked:m,disabled:l,control:g,setControl:x,name:c,form:s,value:f,hasConsumerStoppedPropagationRef:y,required:d,defaultChecked:a,isFormControl:w,bubbleInput:b,setBubbleInput:k};return(0,t.jsx)(ni,{scope:r,...j,children:"function"==typeof p?p(j):o})}var nl="SwitchTrigger",ns=n.forwardRef(({__scopeSwitch:e,onClick:n,...r},i)=>{let{value:o,disabled:a,checked:c,required:u,setControl:d,setChecked:f,hasConsumerStoppedPropagationRef:p,isFormControl:h,bubbleInput:m}=no(nl,e),g=(0,s.useComposedRefs)(i,d);return(0,t.jsx)(x.Primitive.button,{type:"button",role:"switch","aria-checked":c,"aria-required":u,"data-state":nh(c),"data-disabled":a?"":void 0,disabled:a,value:o,...r,ref:g,onClick:(0,l.composeEventHandlers)(n,e=>{f(e=>!e),m&&h&&(p.current=e.isPropagationStopped(),p.current||e.stopPropagation())})})});ns.displayName=nl;var nc=n.forwardRef((e,n)=>{let{__scopeSwitch:r,name:i,checked:o,defaultChecked:a,required:l,disabled:s,value:c,onCheckedChange:u,form:d,...f}=e;return(0,t.jsx)(na,{__scopeSwitch:r,checked:o,defaultChecked:a,disabled:s,required:l,onCheckedChange:u,name:i,form:d,value:c,internal_do_not_use_render:({isFormControl:e})=>(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(ns,{...f,ref:n,__scopeSwitch:r}),e&&(0,t.jsx)(np,{__scopeSwitch:r})]})})});nc.displayName=nt;var nu="SwitchThumb",nd=n.forwardRef((e,n)=>{let{__scopeSwitch:r,...i}=e,o=no(nu,r);return(0,t.jsx)(x.Primitive.span,{"data-state":nh(o.checked),"data-disabled":o.disabled?"":void 0,...i,ref:n})});nd.displayName=nu;var nf="SwitchBubbleInput",np=n.forwardRef(({__scopeSwitch:e,...r},i)=>{let o,{control:a,hasConsumerStoppedPropagationRef:l,checked:c,defaultChecked:u,required:d,disabled:f,name:p,value:h,form:m,bubbleInput:g,setBubbleInput:b}=no(nf,e),v=(0,s.useComposedRefs)(i,b),k=(o=n.useRef({value:c,previous:c}),n.useMemo(()=>(o.current.value!==c&&(o.current.previous=o.current.value,o.current.value=c),o.current.previous),[c])),y=(0,ne.useSize)(a);n.useEffect(()=>{if(!g)return;let e=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,"checked").set,t=!l.current;if(k!==c&&e){let n=new Event("click",{bubbles:t});e.call(g,c),g.dispatchEvent(n)}},[g,k,c,l]);let w=n.useRef(c);return(0,t.jsx)(x.Primitive.input,{type:"checkbox","aria-hidden":!0,defaultChecked:u??w.current,required:d,disabled:f,name:p,value:h,form:m,...r,tabIndex:-1,ref:v,style:{...r.style,...y,position:"absolute",pointerEvents:"none",opacity:0,margin:0,transform:"translateX(-100%)"}})});function nh(e){return e?"checked":"unchecked"}np.displayName=nf,e.s(["Root",0,nc,"Switch",0,nc,"SwitchThumb",0,nd,"Thumb",0,nd,"createSwitchScope",0,nr,"unstable_BubbleInput",0,np,"unstable_Provider",0,na,"unstable_SwitchBubbleInput",0,np,"unstable_SwitchProvider",0,na,"unstable_SwitchTrigger",0,ns,"unstable_Trigger",0,ns],57287);var nm=e.i(57287),nm=nm;function ng({className:e,size:n="default",...r}){return(0,t.jsx)(nm.Root,{"data-slot":"switch","data-size":n,className:(0,G.cn)("peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",e),...r,children:(0,t.jsx)(nm.Thumb,{"data-slot":"switch-thumb",className:(0,G.cn)("pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground")})})}var nx=e.i(602869);let nb=({accessToken:e,selectedServers:r,onChange:i})=>{let[o,a]=(0,n.useState)([]),[l,s]=(0,n.useState)(!0),[c,u]=(0,n.useState)(new Set);(0,n.useEffect)(()=>{let t=!1;return(async()=>{s(!0);try{let n=await (0,nx.fetchMCPServers)(e);if(t)return;let r=Array.isArray(n)?n:n?.data??[];a(r)}catch{t||a([])}finally{t||s(!1)}})(),()=>{t=!0}},[e]);let d=async(t,n)=>{if(!n)return void i(r.filter(e=>e!==t));u(e=>new Set(e).add(t));try{let n=await (0,nx.listMCPTools)(e,t);if(n?.error)return void en.default.warning(`Could not load tools for ${t} \u2014 it will be excluded from this message.`);i([...r,t])}catch{en.default.warning(`Could not load tools for ${t} \u2014 it will be excluded from this message.`)}finally{u(e=>{let n=new Set(e);return n.delete(t),n})}};return(0,t.jsx)("div",{className:"max-w-[320px] max-h-[400px] overflow-y-auto py-2",children:l?(0,t.jsx)("div",{className:"flex flex-col gap-1",children:Array.from({length:3}).map((e,n)=>(0,t.jsxs)("div",{className:"flex items-center justify-between px-3 py-2 gap-3",children:[(0,t.jsxs)("div",{className:"flex items-start gap-3 flex-1 min-w-0",children:[(0,t.jsx)(Y.Skeleton,{className:"h-6 w-6 rounded-md shrink-0"}),(0,t.jsxs)("div",{className:"flex flex-col gap-1.5 flex-1 min-w-0",children:[(0,t.jsx)(Y.Skeleton,{className:"h-3.5 w-24"}),(0,t.jsx)(Y.Skeleton,{className:"h-3 w-32"})]})]}),(0,t.jsx)(Y.Skeleton,{className:"h-3.5 w-6 rounded-full shrink-0"})]},n))}):0===o.length?(0,t.jsx)("div",{className:"px-3 py-4 text-muted-foreground text-[13px] text-center",children:"No MCP servers configured"}):o.map(e=>{let n=e.server_name??e.alias??e.server_id,i=r.includes(n),o=c.has(n);return(0,t.jsxs)("div",{className:"flex items-start justify-between px-3 py-2 gap-3",children:[e.mcp_info?.logo_url&&(0,t.jsx)("img",{src:e.mcp_info.logo_url,alt:`${n} logo`,className:"w-6 h-6 rounded-md object-contain shrink-0 mt-0.5",onError:e=>{e.target.style.display="none"}}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("div",{className:"font-medium text-[13px] text-foreground truncate",children:n}),e.description&&(0,t.jsx)("div",{className:"text-xs text-muted-foreground mt-0.5 truncate",children:e.description})]}),(0,t.jsx)("div",{className:"relative shrink-0",children:o?(0,t.jsx)(t7.Loader2,{className:"h-4 w-4 animate-spin text-muted-foreground"}):(0,t.jsx)(ng,{checked:i,onCheckedChange:e=>d(n,e),className:"scale-75"})})]},e.server_id)})})};var nv=e.i(695411),nk=e.i(459161),ny=e.i(916925);let nw=["Write","Learn","Code","Brainstorm"],nj="litellm_chat_selected_model";function nC(){let e=new Date().getHours();return e>=5&&e<12?"Good morning":e>=12&&e<17?"Good afternoon":"Good evening"}function nS(e){if(!e)return"";let t=e.toLowerCase(),n=t.indexOf("/");return n>0?t.slice(0,n):t.includes("claude")?"anthropic":t.includes("gemini")?"gemini":t.includes("gpt")||t.includes("chatgpt")||/^o[0-9]/.test(t)?"openai":t.includes("mistral")||t.includes("codestral")?"mistral":t.includes("llama")?"meta_llama":t.includes("deepseek")?"deepseek":t.includes("grok")?"xai":t.includes("command")?"cohere":t.includes("nova")||t.includes("titan")?"bedrock":""}e.s(["default",0,function(){let e=(0,er.useRouter)(),{accessToken:l,userId:s,userEmail:c,selectedMCPServers:u,setSelectedMCPServers:d,activeConversationId:f,activeConversation:p,storageUnavailable:h,staleId:m,createConversation:g,appendMessage:x,updateLastAssistantMessage:b,truncateFromMessage:v}=(0,ei.useChatShell)();(0,n.useRef)(null!==f);let[k,y]=(0,n.useState)(null),[w,j]=(0,n.useState)([]),[C,S]=(0,n.useState)(!0),[N,E]=(0,n.useState)(!1),[R,A]=(0,n.useState)(""),[D,P]=(0,n.useState)(null),[T,L]=(0,n.useState)(f),[O,F]=(0,n.useState)(!1),[z,_]=(0,n.useState)(""),[M,I]=(0,n.useState)(!1),[H,B]=(0,n.useState)(!1),$=(0,n.useRef)(null),W=(0,n.useRef)(null),q=(0,n.useRef)(null),[U,K]=(0,n.useState)(!1),V=(0,n.useRef)(null);(0,n.useEffect)(()=>{m&&e.replace(eo.CHAT_ROUTES.chats)},[m,e]),(0,n.useEffect)(()=>{l&&(0,nv.fetchAvailableModels)(l).then(e=>{let t=(e||[]).map(e=>e.model_group??"").filter(Boolean);j(t);try{let e=localStorage.getItem(nj);if(e&&t.includes(e))return void y(e)}catch{}t.length>0&&(y(t[0]),localStorage.setItem(nj,t[0]))}).catch(()=>en.default.error("Could not load models")).finally(()=>S(!1))},[l]),f!==T&&(L(f),P(null));let G=(0,n.useCallback)(e=>{y(e),localStorage.setItem(nj,e),E(!1),A("")},[]),ea=(0,n.useCallback)(async(t,n)=>{let r=t.trim();if(!r||!k||O)return;_("");let i=f;i||(i=g(k),P(null),e.push(`${eo.CHAT_ROUTES.chats}?id=${i}`)),x(i,{role:"user",content:r}),x(i,{role:"assistant",content:""}),F(!0),$.current=new AbortController,n&&P(null);let o=n?null:D,a=n?[...n,{role:"user",content:r}]:o?[{role:"user",content:r}]:[...(p?.messages??[]).filter(e=>"user"===e.role||"assistant"===e.role).map(e=>({role:e.role,content:e.content})),{role:"user",content:r}],s="",c="",d=[],h=!1;try{await (0,nk.makeOpenAIResponsesRequest)(a,(e,t)=>{s+=t,b(i,{content:s})},k,l,void 0,$.current.signal,e=>{c+=e,b(i,{reasoningContent:c})},void 0,void 0,void 0,void 0,void 0,void 0,u.length>0?u:void 0,o,e=>P(e),e=>{d.push(e)}),h=!0}catch(e){e instanceof Error&&"AbortError"===e.name?b(i,{content:s+" [stopped]"}):b(i,{content:"[Something went wrong. The partial response has been saved.]"})}finally{d.length>0&&h&&b(i,{mcpEvents:d}),F(!1),$.current=null}},[f,p,k,u,l,g,x,b,e,O,D]),el=(0,n.useCallback)(()=>{$.current?.abort()},[]),es=(0,n.useCallback)((e,t)=>{if(!f||O)return;let n=p?.messages??[],r=n.findIndex(t=>t.id===e),i=(-1===r?n:n.slice(0,r)).filter(e=>"user"===e.role||"assistant"===e.role).map(e=>({role:e.role,content:e.content}));v(f,e),ea(t,i)},[f,O,p,v,ea]),ec=e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),ea(z))};(0,n.useEffect)(()=>{let e=W.current;e&&(e.style.height="auto",e.style.height=`${Math.min(e.scrollHeight,180)}px`)},[z]),(0,n.useEffect)(()=>{let e=q.current;if(!e)return;let t=()=>{K(e.scrollHeight-e.scrollTop-e.clientHeight>120),null!==V.current&&(V.current=e.scrollTop)};return e.addEventListener("scroll",t,{passive:!0}),()=>e.removeEventListener("scroll",t)},[p]),(0,n.useEffect)(()=>{let e=q.current;O?V.current=e?.scrollTop??0:V.current=null},[O]),(0,n.useLayoutEffect)(()=>{if(null===V.current)return;let e=q.current;e&&(e.scrollTop=V.current)});let eu=(0,n.useRef)(0);(0,n.useLayoutEffect)(()=>{let e=p?.messages?.length??0,t=eu.current;if(eu.current=e,e>t){let e=q.current;e&&(e.scrollTop=e.scrollHeight)}},[p?.messages]);let ed=!p||0===p.messages.length,ef=c?.split("@")[0]??s??"",ep=ef?`${nC()}, ${ef}`:nC(),eh=(R?w.filter(e=>e.toLowerCase().includes(R.toLowerCase())):w).sort((e,t)=>e===k?-1:+(t===k)),em=(0,t.jsxs)("div",{className:"w-[280px] h-[400px] flex flex-col overflow-hidden",children:[(0,t.jsx)("div",{className:"p-2 pb-1",children:(0,t.jsx)(ee.Input,{autoFocus:!0,value:R,onChange:e=>A(e.target.value),placeholder:"Search models...",className:"h-8 text-[13px]"})}),(0,t.jsx)(Q.ScrollArea,{className:"flex-1 h-0",children:eh.map(e=>{let n=e===k,r=nS(e),{logo:i}=r?(0,ny.getProviderLogoAndName)(r):{logo:""};return(0,t.jsxs)(et.Button,{variant:"ghost",onClick:()=>G(e),className:`h-auto w-full justify-start gap-2 rounded px-3 py-[7px] font-normal ${n?"bg-accent":""}`,children:[i?(0,t.jsx)("img",{src:i,alt:"",className:"w-4 h-4 object-contain shrink-0",onError:e=>{e.currentTarget.style.display="none"}}):(0,t.jsx)("span",{className:"w-4 shrink-0"}),(0,t.jsx)("span",{className:"flex-1 text-left text-[13px] text-foreground overflow-hidden text-ellipsis whitespace-nowrap",children:e}),n&&(0,t.jsx)(o.Check,{className:"h-3.5 w-3.5 text-primary shrink-0"})]},e)})})]}),eg=C?(0,t.jsx)(Y.Skeleton,{className:"w-40 h-8"}):(0,t.jsxs)(Z,{open:N,onOpenChange:e=>{E(e),e||A("")},children:[(0,t.jsx)(J,{asChild:!0,children:(0,t.jsxs)(et.Button,{variant:"outline",size:"sm",className:"max-w-[240px] justify-start gap-1.5 overflow-hidden",children:[k?(0,t.jsxs)(t.Fragment,{children:[(()=>{let e=nS(k),{logo:n}=e?(0,ny.getProviderLogoAndName)(e):{logo:""};return n?(0,t.jsx)("img",{src:n,alt:"",className:"w-4 h-4 object-contain shrink-0",onError:e=>{e.currentTarget.style.display="none"}}):null})(),(0,t.jsx)("span",{className:"overflow-hidden text-ellipsis whitespace-nowrap",children:k})]}):(0,t.jsx)("span",{className:"text-muted-foreground",children:"Select model"}),(0,t.jsx)(i.ChevronDown,{className:"h-3 w-3 text-muted-foreground shrink-0"})]})}),(0,t.jsx)(X,{align:"start",side:"top",className:"p-0 w-auto",children:em})]}),ex=e=>(0,t.jsxs)("div",{className:"bg-background rounded-xl border shadow-[0_1px_6px_rgba(0,0,0,0.06)] overflow-hidden",children:[(0,t.jsx)("textarea",{ref:W,value:z,onChange:e=>_(e.target.value),onKeyDown:ec,placeholder:e?"Send a message...":"How can I help you today?",className:"w-full border-none outline-none resize-none text-[15px] text-foreground bg-transparent font-[inherit] box-border",style:{minHeight:e?52:80,padding:e?"16px 20px 8px":"20px 20px 8px"}}),(0,t.jsxs)("div",{className:"flex items-center justify-between border-t",style:{padding:e?"4px 12px 10px":"8px 12px 12px"},children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 min-w-0",children:[eg,(0,t.jsxs)(Z,{open:M,onOpenChange:I,children:[(0,t.jsx)(J,{asChild:!0,children:(0,t.jsxs)(et.Button,{variant:"outline",size:"sm",className:"gap-1 px-2.5 text-muted-foreground",children:[(0,t.jsx)(r.Plus,{className:"h-3.5 w-3.5"}),u.length>0&&(0,t.jsx)("span",{className:"text-xs text-primary font-medium",children:u.length})]})}),(0,t.jsx)(X,{side:"top",align:"start",className:"p-0 w-auto",children:(0,t.jsx)(nb,{accessToken:l,selectedServers:u,onChange:d})})]})]}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[e&&u.length>0&&(0,t.jsxs)("span",{className:"text-xs text-muted-foreground max-w-[160px] overflow-hidden text-ellipsis whitespace-nowrap",children:[u.length," tool",u.length>1?"s":""," connected"]}),O?(0,t.jsx)(et.Button,{variant:"outline",size:"icon-sm",onClick:el,className:"rounded-full shrink-0",children:(0,t.jsx)("div",{className:"w-2.5 h-2.5 bg-foreground rounded-[2px]"})}):(0,t.jsx)(et.Button,{size:"sm",onClick:()=>ea(z),disabled:!z.trim()||C||!k,children:"Send"})]})]})]});return(0,t.jsxs)(t.Fragment,{children:[h&&!H&&(0,t.jsxs)("div",{className:"bg-amber-50 border-b border-amber-200 px-5 py-1.5 text-[13px] text-amber-800 flex justify-between items-center",children:[(0,t.jsx)("span",{children:"Chat history won't be saved in this browser session"}),(0,t.jsx)(et.Button,{variant:"ghost",size:"icon-xs",onClick:()=>B(!0),className:"text-amber-800 hover:bg-amber-100 hover:text-amber-800",children:(0,t.jsx)(a.X,{className:"size-3.5"})})]}),(0,t.jsx)("div",{className:"flex-1 min-h-0 overflow-hidden flex flex-col bg-background",children:ed?(0,t.jsxs)("div",{className:"flex-1 flex flex-col items-center justify-center px-6 pb-20",children:[(0,t.jsx)("h1",{className:"m-0 mb-8 text-[28px] font-semibold text-foreground tracking-tight text-center",children:ep}),(0,t.jsxs)("p",{className:"-mt-4 mb-7 text-sm text-muted-foreground text-center max-w-[520px] leading-relaxed",children:["Chat with 100+ LLMs + MCP tools; authenticate once, use them here."," ",(0,t.jsx)(et.Button,{variant:"link",onClick:()=>e.push(eo.CHAT_ROUTES.integrations),className:"h-auto p-0 text-sm font-medium",children:"Open Integrations ->"})]}),(0,t.jsx)("div",{className:"w-full max-w-[680px]",children:ex(!1)}),(0,t.jsx)("div",{className:"flex gap-2 mt-3.5 flex-wrap justify-center",children:nw.map(e=>(0,t.jsx)(et.Button,{variant:"outline",size:"sm",onClick:()=>_(e+": "),className:"rounded-full px-4 text-muted-foreground",children:e},e))})]}):(0,t.jsxs)("div",{className:"flex-1 min-h-0 flex flex-col mx-auto w-full px-6 relative",style:{maxWidth:760},children:[(0,t.jsx)("div",{ref:q,className:"flex-1 min-h-0 overflow-auto pt-6",style:{overflowAnchor:"none"},children:(0,t.jsx)(t8,{messages:p.messages,isStreaming:O,onEditMessage:es})}),U&&(0,t.jsx)(et.Button,{variant:"ghost",size:"icon",onClick:()=>{let e=q.current;e&&(e.scrollTo({top:e.scrollHeight,behavior:"smooth"}),null!==V.current&&(V.current=e.scrollHeight))},className:"absolute bottom-[100px] left-1/2 -translate-x-1/2 z-10 rounded-full border bg-background/75 text-muted-foreground shadow-sm backdrop-blur-md hover:bg-background/95 hover:text-muted-foreground","aria-label":"Scroll to bottom",children:(0,t.jsx)(i.ChevronDown,{className:"h-3 w-3"})}),(0,t.jsx)("div",{className:"py-3 pb-6",children:ex(!0)})]})})]})}],321443)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/06w8_.601z7_i.js b/litellm/proxy/_experimental/out/_next/static/chunks/06w8_.601z7_i.js new file mode 100644 index 00000000000..90ccedf9166 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/06w8_.601z7_i.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,229315,e=>{"use strict";let t;function r(){return"u">typeof window}function n(e){return u(e)?(e.nodeName||"").toLowerCase():"#document"}function i(e){var t;return(null==e||null==(t=e.ownerDocument)?void 0:t.defaultView)||window}function s(e){var t;return null==(t=(u(e)?e.ownerDocument:e.document)||window.document)?void 0:t.documentElement}function u(e){return!!r()&&(e instanceof Node||e instanceof i(e).Node)}function o(e){return!!r()&&(e instanceof Element||e instanceof i(e).Element)}function a(e){return!!r()&&(e instanceof HTMLElement||e instanceof i(e).HTMLElement)}function l(e){return!(!r()||"u"!!e&&"none"!==e;function m(e){let t=o(e)?y(e):e;return p(t.transform)||p(t.translate)||p(t.scale)||p(t.rotate)||p(t.perspective)||!v()&&(p(t.backdropFilter)||p(t.filter))||d.test(t.willChange||"")||h.test(t.contain||"")}function v(){return null==t&&(t="u">typeof CSS&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),t}function g(e){return/^(html|body|#document)$/.test(n(e))}function y(e){return i(e).getComputedStyle(e)}function b(e){if("html"===n(e))return e;let t=e.assignedSlot||e.parentNode||l(e)&&e.host||s(e);return l(t)?t.host:t}function R(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}e.s(["getComputedStyle",0,y,"getContainingBlock",0,function(e){let t=b(e);for(;a(t)&&!g(t);){if(m(t))return t;if(f(t))break;t=b(t)}return null},"getDocumentElement",0,s,"getFrameElement",0,R,"getNodeName",0,n,"getNodeScroll",0,function(e){return o(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}},"getOverflowAncestors",0,function e(t,r,n){var s;void 0===r&&(r=[]),void 0===n&&(n=!0);let u=function e(t){let r=b(t);return g(r)?t.ownerDocument?t.ownerDocument.body:t.body:a(r)&&c(r)?r:e(r)}(t),o=u===(null==(s=t.ownerDocument)?void 0:s.body),l=i(u);if(!o)return r.concat(u,e(u,[],n));{let t=R(l);return r.concat(l,l.visualViewport||[],c(u)?u:[],t&&n?e(t):[])}},"getParentNode",0,b,"getWindow",0,i,"isContainingBlock",0,m,"isElement",0,o,"isHTMLElement",0,a,"isLastTraversableNode",0,g,"isNode",0,u,"isOverflowElement",0,c,"isShadowRoot",0,l,"isTableElement",0,function(e){return/^(table|td|th)$/.test(n(e))},"isTopLayer",0,f,"isWebKit",0,v])},475254,e=>{"use strict";var t=e.i(271645);let r=e=>{let t=e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,r)=>r?r.toUpperCase():t.toLowerCase());return t.charAt(0).toUpperCase()+t.slice(1)},n=(...e)=>e.filter((e,t,r)=>!!e&&""!==e.trim()&&r.indexOf(e)===t).join(" ").trim();var i={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};let s=(0,t.forwardRef)(({color:e="currentColor",size:r=24,strokeWidth:s=2,absoluteStrokeWidth:u,className:o="",children:a,iconNode:l,...c},f)=>(0,t.createElement)("svg",{ref:f,...i,width:r,height:r,stroke:e,strokeWidth:u?24*Number(s)/Number(r):s,className:n("lucide",o),...!a&&!(e=>{for(let t in e)if(t.startsWith("aria-")||"role"===t||"title"===t)return!0})(c)&&{"aria-hidden":"true"},...c},[...l.map(([e,r])=>(0,t.createElement)(e,r)),...Array.isArray(a)?a:[a]]));e.s(["default",0,(e,i)=>{let u=(0,t.forwardRef)(({className:u,...o},a)=>(0,t.createElement)(s,{ref:a,iconNode:i,className:n(`lucide-${r(e).replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}`,`lucide-${e}`,u),...o}));return u.displayName=r(e),u}],475254)},618566,(e,t,r)=>{t.exports=e.r(976562)},266027,869230,469637,e=>{"use strict";let t;var r=e.i(175555),n=e.i(273911),i=e.i(540143),s=e.i(286491),u=e.i(915823),o=e.i(793803),a=e.i(619273),l=e.i(180166),c=class extends u.Subscribable{constructor(e,t){super(),this.options=t,this.#e=e,this.#t=null,this.#r=(0,o.pendingThenable)(),this.bindMethods(),this.setOptions(t)}#e;#n=void 0;#i=void 0;#s=void 0;#u;#o;#r;#t;#a;#l;#c;#f;#d;#h;#p=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){1===this.listeners.size&&(this.#n.addObserver(this),f(this.#n,this.options)?this.#m():this.updateResult(),this.#v())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return d(this.#n,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return d(this.#n,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#g(),this.#y(),this.#n.removeObserver(this)}setOptions(e){let t=this.options,r=this.#n;if(this.options=this.#e.defaultQueryOptions(e),void 0!==this.options.enabled&&"boolean"!=typeof this.options.enabled&&"function"!=typeof this.options.enabled&&"boolean"!=typeof(0,a.resolveQueryBoolean)(this.options.enabled,this.#n))throw Error("Expected enabled to be a boolean or a callback that returns a boolean");this.#b(),this.#n.setOptions(this.options),t._defaulted&&!(0,a.shallowEqualObjects)(this.options,t)&&this.#e.getQueryCache().notify({type:"observerOptionsUpdated",query:this.#n,observer:this});let n=this.hasListeners();n&&h(this.#n,r,this.options,t)&&this.#m(),this.updateResult(),n&&(this.#n!==r||(0,a.resolveQueryBoolean)(this.options.enabled,this.#n)!==(0,a.resolveQueryBoolean)(t.enabled,this.#n)||(0,a.resolveStaleTime)(this.options.staleTime,this.#n)!==(0,a.resolveStaleTime)(t.staleTime,this.#n))&&this.#R();let i=this.#w();n&&(this.#n!==r||(0,a.resolveQueryBoolean)(this.options.enabled,this.#n)!==(0,a.resolveQueryBoolean)(t.enabled,this.#n)||i!==this.#h)&&this.#S(i)}getOptimisticResult(e){var t,r;let n=this.#e.getQueryCache().build(this.#e,e),i=this.createResult(n,e);return t=this,r=i,(0,a.shallowEqualObjects)(t.getCurrentResult(),r)||(this.#s=i,this.#o=this.options,this.#u=this.#n.state),i}getCurrentResult(){return this.#s}trackResult(e,t){return new Proxy(e,{get:(e,r)=>(this.trackProp(r),t?.(r),"promise"===r&&(this.trackProp("data"),this.options.experimental_prefetchInRender||"pending"!==this.#r.status||this.#r.reject(Error("experimental_prefetchInRender feature flag is not enabled"))),Reflect.get(e,r))})}trackProp(e){this.#p.add(e)}getCurrentQuery(){return this.#n}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),r=this.#e.getQueryCache().build(this.#e,t);return r.fetch().then(()=>this.createResult(r,t))}fetch(e){return this.#m({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#s))}#m(e){this.#b();let t=this.#n.fetch(this.options,e);return e?.throwOnError||(t=t.catch(a.noop)),t}#R(){this.#g();let e=(0,a.resolveStaleTime)(this.options.staleTime,this.#n);if(n.environmentManager.isServer()||this.#s.isStale||!(0,a.isValidTimeout)(e))return;let t=(0,a.timeUntilStale)(this.#s.dataUpdatedAt,e);this.#f=l.timeoutManager.setTimeout(()=>{this.#s.isStale||this.updateResult()},t+1)}#w(){return("function"==typeof this.options.refetchInterval?this.options.refetchInterval(this.#n):this.options.refetchInterval)??!1}#S(e){this.#y(),this.#h=e,!n.environmentManager.isServer()&&!1!==(0,a.resolveQueryBoolean)(this.options.enabled,this.#n)&&(0,a.isValidTimeout)(this.#h)&&0!==this.#h&&(this.#d=l.timeoutManager.setInterval(()=>{(this.options.refetchIntervalInBackground||r.focusManager.isFocused())&&this.#m()},this.#h))}#v(){this.#R(),this.#S(this.#w())}#g(){void 0!==this.#f&&(l.timeoutManager.clearTimeout(this.#f),this.#f=void 0)}#y(){void 0!==this.#d&&(l.timeoutManager.clearInterval(this.#d),this.#d=void 0)}createResult(e,t){let r,n=this.#n,i=this.options,u=this.#s,l=this.#u,c=this.#o,d=e!==n?e.state:this.#i,{state:m}=e,v={...m},g=!1;if(t._optimisticResults){let r=this.hasListeners(),u=!r&&f(e,t),o=r&&h(e,n,t,i);(u||o)&&(v={...v,...(0,s.fetchState)(m.data,e.options)}),"isRestoring"===t._optimisticResults&&(v.fetchStatus="idle")}let{error:y,errorUpdatedAt:b,status:R}=v;r=v.data;let w=!1;if(void 0!==t.placeholderData&&void 0===r&&"pending"===R){let e;u?.isPlaceholderData&&t.placeholderData===c?.placeholderData?(e=u.data,w=!0):e="function"==typeof t.placeholderData?t.placeholderData(this.#c?.state.data,this.#c):t.placeholderData,void 0!==e&&(R="success",r=(0,a.replaceData)(u?.data,e,t),g=!0)}if(t.select&&void 0!==r&&!w)if(u&&r===l?.data&&t.select===this.#a)r=this.#l;else try{this.#a=t.select,r=t.select(r),r=(0,a.replaceData)(u?.data,r,t),this.#l=r,this.#t=null}catch(e){this.#t=e}this.#t&&(y=this.#t,r=this.#l,b=Date.now(),R="error");let S="fetching"===v.fetchStatus,E="pending"===R,C="error"===R,I=E&&S,k=void 0!==r,O={status:R,fetchStatus:v.fetchStatus,isPending:E,isSuccess:"success"===R,isError:C,isInitialLoading:I,isLoading:I,data:r,dataUpdatedAt:v.dataUpdatedAt,error:y,errorUpdatedAt:b,failureCount:v.fetchFailureCount,failureReason:v.fetchFailureReason,errorUpdateCount:v.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:v.dataUpdateCount>d.dataUpdateCount||v.errorUpdateCount>d.errorUpdateCount,isFetching:S,isRefetching:S&&!E,isLoadingError:C&&!k,isPaused:"paused"===v.fetchStatus,isPlaceholderData:g,isRefetchError:C&&k,isStale:p(e,t),refetch:this.refetch,promise:this.#r,isEnabled:!1!==(0,a.resolveQueryBoolean)(t.enabled,e)};if(this.options.experimental_prefetchInRender){let t=void 0!==O.data,r="error"===O.status&&!t,i=e=>{r?e.reject(O.error):t&&e.resolve(O.data)},s=()=>{i(this.#r=O.promise=(0,o.pendingThenable)())},u=this.#r;switch(u.status){case"pending":e.queryHash===n.queryHash&&i(u);break;case"fulfilled":(r||O.data!==u.value)&&s();break;case"rejected":r&&O.error===u.reason||s()}}return O}updateResult(){let e=this.#s,t=this.createResult(this.#n,this.options);if(this.#u=this.#n.state,this.#o=this.options,void 0!==this.#u.data&&(this.#c=this.#n),(0,a.shallowEqualObjects)(t,e))return;this.#s=t;let r=()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,r="function"==typeof t?t():t;if("all"===r||!r&&!this.#p.size)return!0;let n=new Set(r??this.#p);return this.options.throwOnError&&n.add("error"),Object.keys(this.#s).some(t=>this.#s[t]!==e[t]&&n.has(t))};this.#E({listeners:r()})}#b(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#n)return;let t=this.#n;this.#n=e,this.#i=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#v()}#E(e){i.notifyManager.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#s)}),this.#e.getQueryCache().notify({query:this.#n,type:"observerResultsUpdated"})})}};function f(e,t){return!1!==(0,a.resolveQueryBoolean)(t.enabled,e)&&void 0===e.state.data&&("error"!==e.state.status||!1!==(0,a.resolveQueryBoolean)(t.retryOnMount,e))||void 0!==e.state.data&&d(e,t,t.refetchOnMount)}function d(e,t,r){if(!1!==(0,a.resolveQueryBoolean)(t.enabled,e)&&"static"!==(0,a.resolveStaleTime)(t.staleTime,e)){let n="function"==typeof r?r(e):r;return"always"===n||!1!==n&&p(e,t)}return!1}function h(e,t,r,n){return(e!==t||!1===(0,a.resolveQueryBoolean)(n.enabled,e))&&(!r.suspense||"error"!==e.state.status)&&p(e,r)}function p(e,t){return!1!==(0,a.resolveQueryBoolean)(t.enabled,e)&&e.isStaleByTime((0,a.resolveStaleTime)(t.staleTime,e))}e.s(["QueryObserver",0,c],869230),e.i(247167);var m=e.i(271645),v=e.i(912598);e.i(843476);var g=m.createContext((t=!1,{clearReset:()=>{t=!1},reset:()=>{t=!0},isReset:()=>t})),y=m.createContext(!1);y.Provider;var b=(e,t,r)=>t.fetchOptimistic(e).catch(()=>{r.clearReset()});function R(e,t,r){let s,u=m.useContext(y),o=m.useContext(g),l=(0,v.useQueryClient)(r),c=l.defaultQueryOptions(e);l.getDefaultOptions().queries?._experimental_beforeQuery?.(c);let f=l.getQueryCache().get(c.queryHash);if(c._optimisticResults=u?"isRestoring":"optimistic",c.suspense){let e=e=>"static"===e?e:Math.max(e??1e3,1e3),t=c.staleTime;c.staleTime="function"==typeof t?(...r)=>e(t(...r)):e(t),"number"==typeof c.gcTime&&(c.gcTime=Math.max(c.gcTime,1e3))}s=f?.state.error&&"function"==typeof c.throwOnError?(0,a.shouldThrowError)(c.throwOnError,[f.state.error,f]):c.throwOnError,(c.suspense||c.experimental_prefetchInRender||s)&&!o.isReset()&&(c.retryOnMount=!1),m.useEffect(()=>{o.clearReset()},[o]);let d=!l.getQueryCache().get(c.queryHash),[h]=m.useState(()=>new t(l,c)),p=h.getOptimisticResult(c),R=!u&&!1!==e.subscribed;if(m.useSyncExternalStore(m.useCallback(e=>{let t=R?h.subscribe(i.notifyManager.batchCalls(e)):a.noop;return h.updateResult(),t},[h,R]),()=>h.getCurrentResult(),()=>h.getCurrentResult()),m.useEffect(()=>{h.setOptions(c)},[c,h]),c?.suspense&&p.isPending)throw b(c,h,o);if((({result:e,errorResetBoundary:t,throwOnError:r,query:n,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&n&&(i&&void 0===e.data||(0,a.shouldThrowError)(r,[e.error,n])))({result:p,errorResetBoundary:o,throwOnError:c.throwOnError,query:f,suspense:c.suspense}))throw p.error;if(l.getDefaultOptions().queries?._experimental_afterQuery?.(c,p),c.experimental_prefetchInRender&&!n.environmentManager.isServer()&&p.isLoading&&p.isFetching&&!u){let e=d?b(c,h,o):f?.promise;e?.catch(a.noop).finally(()=>{h.updateResult()})}return c.notifyOnChangeProps?p:h.trackResult(p)}e.s(["useBaseQuery",0,R],469637),e.s(["useQuery",0,function(e,t){return R(e,c,t)}],266027)},612256,243652,e=>{"use strict";var t=e.i(602869),r=e.i(266027);function n(e){let t=[e];return{all:t,lists:()=>[...t,"list"],list:e=>[...t,"list",{params:e}],details:()=>[...t,"detail"],detail:e=>[...t,"detail",e]}}e.s(["createQueryKeys",0,n],243652);let i=n("uiConfig");e.s(["useUIConfig",0,()=>(0,r.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,t.getUiConfig)(),staleTime:864e5,gcTime:864e5})],612256)},321836,e=>{"use strict";let t="litellm_return_url",r="redirect_to";function n(){return window.location.href}function i(){if("u"typeof document&&(document.cookie=`${t}=; path=/; max-age=0`)}catch(e){console.error("Failed to clear return URL cookie:",e)}}function u(){return new URLSearchParams(window.location.search).get(r)}function o(){let e=window.location.hostname;return"localhost"===e||"127.0.0.1"===e||"::1"===e||e.startsWith("127.")||e.endsWith(".local")}function a(e){if(!e)return!1;if(e.startsWith("/")&&!e.startsWith("//"))return!0;try{let t=new URL(e),r=window.location.hostname;if(t.hostname!==r)return!1;if(o())return!0;return t.origin===window.location.origin}catch{return!1}}e.s(["buildLoginUrlWithReturn",0,function(e,t){let i=t||n();if(!i||i.includes("/login"))return e;let s=e.includes("?")?"&":"?";return`${e}${s}${r}=${encodeURIComponent(i)}`},"clearStoredReturnUrl",0,s,"consumeReturnUrl",0,function(){let e=u();if(e){if(a(e))return s(),e;o()&&console.warn("[returnUrlUtils] Invalid return URL in params rejected:",e)}let t=i();if(t){if(a(t))return s(),t;o()&&console.warn("[returnUrlUtils] Invalid return URL in cookie rejected:",t)}return null},"getReturnUrl",0,function(){let e=u();if(e)return e;let t=i();return t||null},"isValidReturnUrl",0,a,"normalizeUrlForCompare",0,function(e){try{let t=new URL(e,window.location.origin),r=t.pathname;r.length>1&&r.endsWith("/")&&(r=r.slice(0,-1));let n=new URLSearchParams(t.search),i=new URLSearchParams;Array.from(n.entries()).sort(([e],[t])=>e.localeCompare(t)).forEach(([e,t])=>{i.append(e,t)});let s=i.toString(),u=t.hash||"";return`${t.origin}${r}${s?`?${s}`:""}${u}`}catch{return e}},"storeReturnUrl",0,function(){let e=n();e&&function(e,t,r=300){if("u"{"use strict";var t=e.i(602869),r=e.i(268004),n=e.i(161281),i=e.i(321836),s=e.i(618566),u=e.i(271645),o=e.i(708347),a=e.i(612256);e.s(["default",0,()=>{let e=(0,s.useRouter)(),{data:l,isLoading:c}=(0,a.useUIConfig)(),f="u">typeof document?(0,r.getCookie)("token"):null,d=(0,u.useMemo)(()=>(0,n.decodeToken)(f),[f]),h=(0,u.useMemo)(()=>(0,n.checkTokenValidity)(f),[f])&&!l?.admin_ui_disabled,p=(0,u.useCallback)(()=>{(0,i.storeReturnUrl)();let r=`${(0,t.getProxyBaseUrl)()}/ui/login`,n=(0,i.buildLoginUrlWithReturn)(r);e.replace(n)},[e]);return(0,u.useEffect)(()=>{!c&&(h||(f&&(0,r.clearTokenCookies)(),p()))},[c,h,f,p]),{isLoading:c,isAuthorized:h,token:h?f:null,accessToken:d?.key??null,userId:d?.user_id??null,userEmail:d?.user_email??null,userRole:(0,o.formatUserRole)(d?.user_role),premiumUser:d?.premium_user??null,disabledPersonalKeyCreation:d?.disabled_non_admin_personal_key_creation??null,showSSOBanner:d?.login_method==="username_password"}}])},563113,887719,e=>{"use strict";var t=e.i(271645),r=e.i(864517),n=e.i(244009),i=e.i(408850),s=e.i(87414);let u=function(...e){let t={};return e.forEach(e=>{e&&Object.keys(e).forEach(r=>{void 0!==e[r]&&(t[r]=e[r])})}),t};function o(e){let{closable:r,closeIcon:n}=e||{};return t.default.useMemo(()=>{if(!r&&(!1===r||!1===n||null===n))return!1;if(void 0===r&&void 0===n)return null;let e={closeIcon:"boolean"!=typeof n&&null!==n?n:void 0};return r&&"object"==typeof r&&(e=Object.assign(Object.assign({},e),r)),e},[r,n])}e.s(["default",0,u],887719);let a={};e.s(["pickClosable",0,function(e){if(!e)return;let{closable:t,closeIcon:r}=e;return{closable:t,closeIcon:r}},"useClosable",0,(e,l,c=a)=>{let f=o(e),d=o(l),[h]=(0,i.useLocale)("global",s.default.global),p="boolean"!=typeof f&&!!(null==f?void 0:f.disabled),m=t.default.useMemo(()=>Object.assign({closeIcon:t.default.createElement(r.default,null)},c),[c]),v=t.default.useMemo(()=>!1!==f&&(f?u(m,d,f):!1!==d&&(d?u(m,d):!!m.closable&&m)),[f,d,m]);return t.default.useMemo(()=>{var e,r;if(!1===v)return[!1,null,p,{}];let{closeIconRender:i}=m,{closeIcon:s}=v,u=s,o=(0,n.default)(v,!0);return null!=u&&(i&&(u=i(s)),u=t.default.isValidElement(u)?t.default.cloneElement(u,Object.assign(Object.assign(Object.assign({},u.props),{"aria-label":null!=(r=null==(e=u.props)?void 0:e["aria-label"])?r:h.close}),o)):t.default.createElement("span",Object.assign({"aria-label":h.close},o),u)),[!0,u,p,o]},[p,h.close,v,m])}],563113)},872855,e=>{"use strict";var t=e.i(271645);let r=t.createContext(void 0);e.s(["useDirection",0,function(){let e=t.useContext(r);return e?.direction??"ltr"}])},73364,e=>{"use strict";var t=e.i(343084),r=e.i(229315);e.s(["getCssDimensions",0,function(e){let n=(0,r.getComputedStyle)(e),i=parseFloat(n.width)||0,s=parseFloat(n.height)||0,u=(0,r.isHTMLElement)(e),o=u?e.offsetWidth:i,a=u?e.offsetHeight:s;return((0,t.round)(i)!==o||(0,t.round)(s)!==a)&&(i=o,s=a),{width:i,height:s}}])},755838,(e,t,r)=>{"use strict";var n=e.r(271645),i="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},s=n.useState,u=n.useEffect,o=n.useLayoutEffect,a=n.useDebugValue;function l(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!i(e,r)}catch(e){return!0}}var c="u"{"use strict";t.exports=e.r(755838)},752822,(e,t,r)=>{"use strict";var n=e.r(271645),i=e.r(802239),s="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},u=i.useSyncExternalStore,o=n.useRef,a=n.useEffect,l=n.useMemo,c=n.useDebugValue;r.useSyncExternalStoreWithSelector=function(e,t,r,n,i){var f=o(null);if(null===f.current){var d={hasValue:!1,value:null};f.current=d}else d=f.current;var h=u(e,(f=l(function(){function e(e){if(!a){if(a=!0,u=e,e=n(e),void 0!==i&&d.hasValue){var t=d.value;if(i(t,e))return o=t}return o=e}if(t=o,s(u,e))return t;var r=n(e);return void 0!==i&&i(t,r)?(u=e,t):(u=e,o=r)}var u,o,a=!1,l=void 0===r?null:r;return[function(){return e(t())},null===l?void 0:function(){return e(l())}]},[t,r,n,i]))[0],f[1]);return a(function(){d.hasValue=!0,d.value=h},[h]),c(h),h}},430224,(e,t,r)=>{"use strict";t.exports=e.r(752822)},214553,e=>{"use strict";let t={...e.i(271645)};e.s(["SafeReact",0,t])},921374,e=>{"use strict";var t=e.i(271645);let r={};e.s(["useRefWithInit",0,function(e,n){let i=t.useRef(r);return i.current===r&&(i.current=e(n)),i}])},667865,e=>{"use strict";var t=e.i(214553),r=e.i(921374);let n=t.SafeReact.useInsertionEffect,i=n&&n!==t.SafeReact.useLayoutEffect?n:e=>e();function s(){let e={next:void 0,callback:u,trampoline:(...t)=>e.callback?.(...t),effect:()=>{e.callback=e.next}};return e}function u(){}e.s(["useStableCallback",0,function(e){let t=(0,r.useRefWithInit)(s).current;return t.next=e,i(t.effect),t.trampoline}])},146376,e=>{"use strict";var t=e.i(271645);let r="u">typeof document?t.useLayoutEffect:()=>{};e.s(["useIsoLayoutEffect",0,r])},435241,e=>{"use strict";e.s(["mergeObjects",0,function(e,t){return e&&!t?e:!e&&t?t:e||t?{...e,...t}:void 0}])},176782,e=>{"use strict";var t=e.i(435241);let r={};function n(e){return u(e)?{...o(e,r)}:function(e){let t={...e};for(let e in t){let r=t[e];s(e,r)&&(t[e]=a(r))}return t}(e)}function i(e,r){return u(r)?o(r,e):function(e,r){if(!r)return e;for(let n in r){let i=r[n];switch(n){case"style":e[n]=(0,t.mergeObjects)(e.style,i);break;case"className":e[n]=c(e.className,i);break;default:s(n,i)?e[n]=function(e,t){return t?e?(...r)=>{let n=r[0];if(f(n)){l(n);let i=t(...r);return n.baseUIHandlerPrevented||e?.(...r),i}let i=t(...r);return e?.(...r),i}:a(t):e}(e[n],i):e[n]=i}}return e}(e,r)}function s(e,t){let r=e.charCodeAt(0),n=e.charCodeAt(1),i=e.charCodeAt(2);return 111===r&&110===n&&i>=65&&i<=90&&("function"==typeof t||void 0===t)}function u(e){return"function"==typeof e}function o(e,t){return u(e)?e(t):e??r}function a(e){return e?(...t)=>{let r=t[0];return f(r)&&l(r),e(...t)}:e}function l(e){return e.preventBaseUIHandler=()=>{e.baseUIHandlerPrevented=!0},e}function c(e,t){return t?e?t+" "+e:t:e}function f(e){return null!=e&&"object"==typeof e&&"nativeEvent"in e}e.s(["makeEventPreventable",0,l,"mergeClassNames",0,c,"mergeProps",0,function(e,t,r,s,u){if(!r&&!s&&!u&&!e)return n(t);let o=n(e);return t&&(o=i(o,t)),r&&(o=i(o,r)),s&&(o=i(o,s)),u&&(o=i(o,u)),o},"mergePropsN",0,function(e){if(0===e.length)return r;if(1===e.length)return n(e[0]);let t=n(e[0]);for(let r=1;r{"use strict";let t=function(e,...t){let r=new URL("https://base-ui.com/production-error");return r.searchParams.set("code",e.toString()),t.forEach(e=>r.searchParams.append("args[]",e)),`Base UI error #${e}; visit ${r} for the full message.`};e.s(["default",0,t])},828918,e=>{"use strict";var t=e.i(921374);function r(){return{callback:null,cleanup:null,refs:[]}}function n(e,t){if(e.refs=t,t.every(e=>null==e)){e.callback=null;return}e.callback=r=>{if(e.cleanup&&(e.cleanup(),e.cleanup=null),null!=r){let n=Array(t.length).fill(null);for(let e=0;e{for(let e=0;ee!==s[t]))&&n(u,e),u.callback}])},958321,e=>{"use strict";let t=parseInt(e.i(271645).version,10);e.s(["isReactVersionAtLeast",0,function(e){return t>=e}])},978554,e=>{"use strict";var t=e.i(271645),r=e.i(958321);e.s(["getReactElementRef",0,function(e){if(!t.isValidElement(e))return null;let n=e.props;return((0,r.isReactVersionAtLeast)(19)?n?.ref:e.ref)??null}])},399627,e=>{"use strict";e.s(["warn",0,function(){}])},956789,e=>{"use strict";let t=Object.freeze([]),r=Object.freeze({});e.s(["EMPTY_ARRAY",0,t,"EMPTY_OBJECT",0,r,"NOOP",0,function(){}])},416919,809835,377570,e=>{"use strict";e.s(["getStateAttributesProps",0,function(e,t){let r={};for(let n in e){let i=e[n];if(t?.hasOwnProperty(n)){let e=t[n](i);null!=e&&Object.assign(r,e);continue}!0===i?r[`data-${n.toLowerCase()}`]="":i&&(r[`data-${n.toLowerCase()}`]=i.toString())}return r}],416919),e.s(["resolveClassName",0,function(e,t){return"function"==typeof e?e(t):e}],809835),e.s(["resolveStyle",0,function(e,t){return"function"==typeof e?e(t):e}],377570)},552245,e=>{"use strict";var t=e.i(733332),r=e.i(271645),n=e.i(828918),i=e.i(978554),s=e.i(435241);e.i(399627);var u=e.i(956789),o=e.i(416919),a=e.i(809835),l=e.i(377570),c=e.i(176782);let f=Symbol.for("react.lazy");e.s(["useRenderElement",0,function(e,d,h={}){let p=d.render,m=function(e,t={}){var r;let{className:f,style:d,render:h}=e,{state:p=u.EMPTY_OBJECT,ref:m,props:v,stateAttributesMapping:g,enabled:y=!0}=t,b=y?(0,a.resolveClassName)(f,p):void 0,R=y?(0,l.resolveStyle)(d,p):void 0,w=y?(0,o.getStateAttributesProps)(p,g):u.EMPTY_OBJECT,S=y&&v?Array.isArray(r=v)?(0,c.mergePropsN)(r):(0,c.mergeProps)(void 0,r):void 0,E=y?(0,s.mergeObjects)(w,S)??{}:u.EMPTY_OBJECT;return("u">typeof document&&(y?Array.isArray(m)?E.ref=(0,n.useMergedRefsN)([E.ref,(0,i.getReactElementRef)(h),...m]):E.ref=(0,n.useMergedRefs)(E.ref,(0,i.getReactElementRef)(h),m):(0,n.useMergedRefs)(null,null)),y)?(void 0!==b&&(E.className=(0,c.mergeClassNames)(E.className,b)),void 0!==R&&(E.style=(0,s.mergeObjects)(E.style,R)),E):u.EMPTY_OBJECT}(d,h);return!1===h.enabled?null:function(e,n,i,s){if(n){if("function"==typeof n)return n(i,s);let e=(0,c.mergeProps)(i,n.props);e.ref=i.ref;let t=n;return t?.$$typeof===f&&(t=r.Children.toArray(n)[0]),r.cloneElement(t,e)}if(e&&"string"==typeof e){var u,o;return u=e,o=i,"button"===u?(0,r.createElement)("button",{type:"button",...o,key:o.key}):"img"===u?(0,r.createElement)("img",{alt:"",...o,key:o.key}):r.createElement(u,o)}throw Error((0,t.default)(8))}(e,p,m,h.state??u.EMPTY_OBJECT)}])},626300,e=>{"use strict";var t=e.i(271645);let r=[];e.s(["useOnMount",0,function(e){t.useEffect(e,r)}])},708445,e=>{"use strict";var t=e.i(921374),r=e.i(626300);let n=new class{callbacks=[];callbacksCount=0;nextId=1;startId=1;isScheduled=!1;tick=e=>{this.isScheduled=!1;let t=this.callbacks,r=this.callbacksCount;if(this.callbacks=[],this.callbacksCount=0,this.startId=this.nextId,r>0)for(let r=0;r=this.callbacks.length||(this.callbacks[t]=null,this.callbacksCount-=1)}};class i{static create(){return new i}static request(e){return n.request(e)}static cancel(e){return n.cancel(e)}currentId=null;request(e){this.cancel(),this.currentId=n.request(()=>{this.currentId=null,e()})}cancel=()=>{null!==this.currentId&&(n.cancel(this.currentId),this.currentId=null)};disposeEffect=()=>this.cancel}e.s(["AnimationFrame",0,i,"useAnimationFrame",0,function(){let e=(0,t.useRefWithInit)(i.create).current;return(0,r.useOnMount)(e.disposeEffect),e}])},594603,e=>{"use strict";e.s(["resolveRef",0,function(e){return null==e?e:"current"in e?e.current:e}])},209407,e=>{"use strict";var t;let r=((t={}).startingStyle="data-starting-style",t.endingStyle="data-ending-style",t),n={[r.startingStyle]:""},i={[r.endingStyle]:""};e.s(["TransitionStatusDataAttributes",0,r,"transitionStatusMapping",0,{transitionStatus:e=>"starting"===e?n:"ending"===e?i:null}])},137584,222640,e=>{"use strict";var t=e.i(271645),r=e.i(667865),n=e.i(174080),i=e.i(708445),s=e.i(594603),u=e.i(209407);function o(e,t=!1,a=!0){let l=(0,i.useAnimationFrame)();return(0,r.useStableCallback)((r,i=null)=>{l.cancel();let o=(0,s.resolveRef)(e);if(null==o)return;let c=()=>{n.flushSync(r)};if("function"!=typeof o.getAnimations||globalThis.BASE_UI_ANIMATIONS_DISABLED)return void r();function f(){Promise.all(o.getAnimations().map(e=>e.finished)).then(()=>{i?.aborted||c()}).catch(()=>{if(a){i?.aborted||c();return}let e=o.getAnimations();!i?.aborted&&e.length>0&&e.some(e=>e.pending||"finished"!==e.playState)&&f()})}if(t){let e=u.TransitionStatusDataAttributes.startingStyle;if(!o.hasAttribute(e))return void l.request(f);let t=new MutationObserver(()=>{o.hasAttribute(e)||(t.disconnect(),f())});return t.observe(o,{attributes:!0,attributeFilter:[e]}),void i?.addEventListener("abort",()=>t.disconnect(),{once:!0})}l.request(f)})}e.s(["useAnimationsFinished",0,o],222640),e.s(["useOpenChangeComplete",0,function(e){let{enabled:n=!0,open:i,ref:s,onComplete:u}=e,a=(0,r.useStableCallback)(u),l=o(s,i,!1);t.useEffect(()=>{if(!n)return;let e=new AbortController;return l(a,e.signal),()=>{e.abort()}},[n,i,a,l])}],137584)},223910,e=>{"use strict";var t=e.i(271645),r=e.i(146376),n=e.i(708445);e.s(["useTransitionStatus",0,function(e,i=!1,s=!1){let[u,o]=t.useState(e&&i?"idle":void 0),[a,l]=t.useState(e);return e&&!a&&(l(!0),o("starting")),e||!a||"ending"===u||s||o("ending"),e||a||"ending"!==u||o(void 0),(0,r.useIsoLayoutEffect)(()=>{if(!e&&a&&"ending"!==u&&s){let e=n.AnimationFrame.request(()=>{o("ending")});return()=>{n.AnimationFrame.cancel(e)}}},[e,a,u,s]),(0,r.useIsoLayoutEffect)(()=>{if(!e||i)return;let t=n.AnimationFrame.request(()=>{o(void 0)});return()=>{n.AnimationFrame.cancel(t)}},[i,e]),(0,r.useIsoLayoutEffect)(()=>{if(!e||!i)return;e&&a&&"idle"!==u&&o("starting");let t=n.AnimationFrame.request(()=>{o("idle")});return()=>{n.AnimationFrame.cancel(t)}},[i,e,a,u]),{mounted:a,setMounted:l,transitionStatus:u}}])},108868,e=>{"use strict";e.s(["ownerDocument",0,function(e){return e?.ownerDocument||document}])},883977,e=>{"use strict";var t=e.i(271645),r=e.i(214553);let n=0,i=r.SafeReact.useId;e.s(["useId",0,function(e,r){if(void 0!==i){let t=i();return e??(r?`${r}-${t}`:t)}return function(e,r="mui"){let[i,s]=t.useState(e),u=e||i;return t.useEffect(()=>{null==i&&(n+=1,s(`${r}-${n}`))},[i,r]),u}(e,r)}])},675606,56434,e=>{"use strict";var t=e.i(956789);e.s(["createChangeEventDetails",0,function(e,r,n,i){let s=!1,u=!1,o=i??t.EMPTY_OBJECT;return{reason:e,event:r??new Event("base-ui"),cancel(){s=!0},allowPropagation(){u=!0},get isCanceled(){return s},get isPropagationAllowed(){return u},trigger:n,...o}}],675606),e.s(["cancelOpen",0,"cancel-open","chipRemovePress",0,"chip-remove-press","clearPress",0,"clear-press","closePress",0,"close-press","closeWatcher",0,"close-watcher","decrementPress",0,"decrement-press","disabled",0,"disabled","drag",0,"drag","escapeKey",0,"escape-key","focusOut",0,"focus-out","imperativeAction",0,"imperative-action","incrementPress",0,"increment-press","initial",0,"initial","inputBlur",0,"input-blur","inputChange",0,"input-change","inputClear",0,"input-clear","inputPaste",0,"input-paste","inputPress",0,"input-press","itemPress",0,"item-press","keyboard",0,"keyboard","linkPress",0,"link-press","listNavigation",0,"list-navigation","missing",0,"missing","none",0,"none","outsidePress",0,"outside-press","pointer",0,"pointer","scrub",0,"scrub","siblingOpen",0,"sibling-open","swipe",0,"swipe","trackPress",0,"track-press","triggerFocus",0,"trigger-focus","triggerHover",0,"trigger-hover","triggerPress",0,"trigger-press","wheel",0,"wheel","windowResize",0,"window-resize"],216856);var r=e.i(216856);e.s(["REASONS",0,r],56434)},647554,e=>{"use strict";var t=e.i(229315);e.s(["activeElement",0,function(e){let t=e.activeElement;for(;t?.shadowRoot?.activeElement!=null;)t=t.shadowRoot.activeElement;return t},"contains",0,function(e,r){if(!e||!r)return!1;let n=r.getRootNode?.();if(e.contains(r))return!0;if(n&&(0,t.isShadowRoot)(n)){let t=r;for(;t;){if(e===t)return!0;t=t.parentNode||t.host}}return!1},"getTarget",0,function(e){return"composedPath"in e?e.composedPath()[0]:e.target}])},788015,e=>{"use strict";var t=e.i(883977);e.s(["useBaseUiId",0,function(e){return(0,t.useId)(e,"base-ui")}])},621082,e=>{"use strict";var t=e.i(229315);function r(e,{startingIndex:t=-1,decrement:i=!1,disabledIndices:s,amount:u=1}={}){let o=t;do o+=i?-u:u;while(o>=0&&o<=e.length-1&&n(e,o,s))return o}function n(e,t,r){if("function"==typeof r?r(t):r?.includes(t)??!1)return!0;let n=e[t];return!!n&&(!i(n)||!r&&(n.hasAttribute("disabled")||"true"===n.getAttribute("aria-disabled")))}function i(e,r=e?(0,t.getComputedStyle)(e):null){var n;return!!e&&!!e.isConnected&&!!r&&"hidden"!==(n=r).visibility&&"collapse"!==n.visibility&&("function"==typeof e.checkVisibility?e.checkVisibility():"none"!==r.display&&"contents"!==r.display)}e.s(["findNonDisabledListIndex",0,r,"getMaxListIndex",0,function(e,t){return r(e.current,{decrement:!0,startingIndex:e.current.length,disabledIndices:t})},"getMinListIndex",0,function(e,t){return r(e.current,{disabledIndices:t})},"isElementVisible",0,i,"isIndexOutOfListBounds",0,function(e,t){return t<0||t>=e.length},"isListIndexDisabled",0,n])},144394,e=>{"use strict";var t=e.i(958321);e.s(["inertValue",0,function(e){return(0,t.isReactVersionAtLeast)(19)?e:e?"true":void 0}])},487486,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(552245),i=e.i(115504);let s=(0,i.cva)({base:"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [&>svg]:pointer-events-none [&>svg]:size-3",variants:{variant:{default:"bg-primary text-primary-foreground [a&]:hover:bg-primary/90",secondary:"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",destructive:"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",outline:"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",ghost:"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",link:"text-primary underline-offset-4 [a&]:hover:underline"}},defaultVariants:{variant:"default"}}),u=r.forwardRef(({className:e,variant:r="default",render:u,...o},a)=>{var l;return l={render:u??(0,t.jsx)("span",{}),ref:a,props:{"data-slot":"badge","data-variant":r,className:(0,i.cn)(s({variant:r}),e),...o}},(0,n.useRenderElement)(l.defaultTagName??"div",l,l)});u.displayName="Badge",e.s(["Badge",0,u],487486)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/06wsz_ii_ixc0.js b/litellm/proxy/_experimental/out/_next/static/chunks/06wsz_ii_ixc0.js new file mode 100644 index 00000000000..6b19ae1c603 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/06wsz_ii_ixc0.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,233538,e=>{"use strict";e.s(["isDisabledReactIssue7711",0,function(e){let t=e.parentElement,r=null;for(;t&&!(t instanceof HTMLFieldSetElement);)t instanceof HTMLLegendElement&&(r=t),t=t.parentElement;let a=(null==t?void 0:t.getAttribute("disabled"))==="";return!(a&&function(e){if(!e)return!1;let t=e.previousElementSibling;for(;null!==t;){if(t instanceof HTMLLegendElement)return!1;t=t.previousElementSibling}return!0}(r))&&a}])},503269,214520,601893,694421,140721,942803,35889,722678,e=>{"use strict";var t=e.i(271645),r=e.i(914189);e.s(["useControllable",0,function(e,a,l){let[s,n]=(0,t.useState)(l),o=void 0!==e,i=(0,t.useRef)(o),d=(0,t.useRef)(!1),c=(0,t.useRef)(!1);return!o||i.current||d.current?o||!i.current||c.current||(c.current=!0,i.current=o,console.error("A component is changing from controlled to uncontrolled. This may be caused by the value changing from a defined value to undefined, which should not happen.")):(d.current=!0,i.current=o,console.error("A component is changing from uncontrolled to controlled. This may be caused by the value changing from undefined to a defined value, which should not happen.")),[o?e:s,(0,r.useEvent)(e=>(o||n(e),null==a?void 0:a(e)))]}],503269),e.s(["useDefaultValue",0,function(e){let[r]=(0,t.useState)(e);return r}],214520);let a=(0,t.createContext)(void 0);function l(){return(0,t.useContext)(a)}e.s(["useDisabled",0,l],601893);var s=e.i(174080),n=e.i(746725);function o(e={},t=null,r=[]){for(let[a,l]of Object.entries(e))!function e(t,r,a){if(Array.isArray(a))for(let[l,s]of a.entries())e(t,i(r,l.toString()),s);else a instanceof Date?t.push([r,a.toISOString()]):"boolean"==typeof a?t.push([r,a?"1":"0"]):"string"==typeof a?t.push([r,a]):"number"==typeof a?t.push([r,`${a}`]):null==a?t.push([r,""]):o(a,r,t)}(r,i(t,a),l);return r}function i(e,t){return e?e+"["+t+"]":t}e.s(["attemptSubmit",0,function(e){var t,r;let a=null!=(t=null==e?void 0:e.form)?t:e.closest("form");if(a){for(let t of a.elements)if(t!==e&&("INPUT"===t.tagName&&"submit"===t.type||"BUTTON"===t.tagName&&"submit"===t.type||"INPUT"===t.nodeName&&"image"===t.type))return void t.click();null==(r=a.requestSubmit)||r.call(a)}},"objectToFormEntries",0,o],694421);var d=e.i(700020),c=e.i(2788);let u=(0,t.createContext)(null);function m({children:e}){let r=(0,t.useContext)(u);if(!r)return t.default.createElement(t.default.Fragment,null,e);let{target:a}=r;return a?(0,s.createPortal)(t.default.createElement(t.default.Fragment,null,e),a):null}function g({setForm:e,formId:r}){return(0,t.useEffect)(()=>{if(r){let t=document.getElementById(r);t&&e(t)}},[e,r]),r?null:t.default.createElement(c.Hidden,{features:c.HiddenFeatures.Hidden,as:"input",type:"hidden",hidden:!0,readOnly:!0,ref:t=>{if(!t)return;let r=t.closest("form");r&&e(r)}})}e.s(["FormFields",0,function({data:e,form:r,disabled:a,onReset:l,overrides:s}){let[i,u]=(0,t.useState)(null),f=(0,n.useDisposables)();return(0,t.useEffect)(()=>{if(l&&i)return f.addEventListener(i,"reset",l)},[i,r,l]),t.default.createElement(m,null,t.default.createElement(g,{setForm:u,formId:r}),o(e).map(([e,l])=>t.default.createElement(c.Hidden,{features:c.HiddenFeatures.Hidden,...(0,d.compact)({key:e,as:"input",type:"hidden",hidden:!0,readOnly:!0,form:r,disabled:a,name:e,value:l,...s})})))}],140721);let f=(0,t.createContext)(void 0);function p(){return(0,t.useContext)(f)}e.s(["useProvidedId",0,p],942803);var b=e.i(835696),h=e.i(294316);let x=(0,t.createContext)(null);x.displayName="DescriptionContext";let v=Object.assign((0,d.forwardRefWithAs)(function(e,r){let a=(0,t.useId)(),s=l(),{id:n=`headlessui-description-${a}`,...o}=e,i=function e(){let r=(0,t.useContext)(x);if(null===r){let t=Error("You used a component, but it is not inside a relevant parent.");throw Error.captureStackTrace&&Error.captureStackTrace(t,e),t}return r}(),c=(0,h.useSyncRefs)(r);(0,b.useIsoMorphicEffect)(()=>i.register(n),[n,i.register]);let u=s||!1,m=(0,t.useMemo)(()=>({...i.slot,disabled:u}),[i.slot,u]),g={ref:c,...i.props,id:n};return(0,d.useRender)()({ourProps:g,theirProps:o,slot:m,defaultTag:"p",name:i.name||"Description"})}),{});e.s(["Description",0,v,"useDescribedBy",0,function(){var e,r;return null!=(r=null==(e=(0,t.useContext)(x))?void 0:e.value)?r:void 0},"useDescriptions",0,function(){let[e,a]=(0,t.useState)([]);return[e.length>0?e.join(" "):void 0,(0,t.useMemo)(()=>function(e){let l=(0,r.useEvent)(e=>(a(t=>[...t,e]),()=>a(t=>{let r=t.slice(),a=r.indexOf(e);return -1!==a&&r.splice(a,1),r}))),s=(0,t.useMemo)(()=>({register:l,slot:e.slot,name:e.name,props:e.props,value:e.value}),[l,e.slot,e.name,e.props,e.value]);return t.default.createElement(x.Provider,{value:s},e.children)},[a])]}],35889);let y=(0,t.createContext)(null);function C(e){var r,a,l;let s=null!=(a=null==(r=(0,t.useContext)(y))?void 0:r.value)?a:void 0;return(null!=(l=null==e?void 0:e.length)?l:0)>0?[s,...e].filter(Boolean).join(" "):s}y.displayName="LabelContext";let k=Object.assign((0,d.forwardRefWithAs)(function(e,a){var s;let n=(0,t.useId)(),o=function e(){let r=(0,t.useContext)(y);if(null===r){let t=Error("You used a
); @@ -323,6 +334,7 @@ const ComplexityRouterConfig: React.FC = ({ matchThreshold={matchThreshold} onMatchThresholdChange={onMatchThresholdChange} modelInfo={modelInfo} + showValidationErrors={showValidationErrors} /> )} diff --git a/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.test.tsx b/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.test.tsx new file mode 100644 index 00000000000..2336e6faf43 --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.test.tsx @@ -0,0 +1,53 @@ +import { renderWithProviders, screen } from "../../../tests/test-utils"; +import userEvent from "@testing-library/user-event"; +import { vi } from "vitest"; +import SemanticKeywordMatching from "./SemanticKeywordMatching"; + +const mockModelInfo = [ + { model_group: "gpt-4", mode: "chat" }, + { model_group: "text-embedding-3-small", mode: "embedding" }, + { model_group: "voyage-3-5", mode: "embedding" }, + { model_group: "legacy-model" }, +] as any[]; + +const baseProps = { + enabled: true, + onEnabledChange: vi.fn(), + embeddingModel: undefined, + onEmbeddingModelChange: vi.fn(), + matchThreshold: 0.5, + onMatchThresholdChange: vi.fn(), + modelInfo: mockModelInfo, +}; + +describe("SemanticKeywordMatching", () => { + it("only lists embedding-mode models in the embedding model dropdown", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const combobox = screen.getByRole("combobox"); + await user.click(combobox); + + expect((await screen.findAllByText("text-embedding-3-small")).length).toBeGreaterThan(0); + expect(screen.getAllByText("voyage-3-5").length).toBeGreaterThan(0); + expect(screen.queryAllByText("gpt-4")).toHaveLength(0); + expect(screen.queryAllByText("legacy-model")).toHaveLength(0); + }); + + it("does not show a validation error by default", () => { + renderWithProviders(); + expect(screen.queryByText("An embedding model is required")).not.toBeInTheDocument(); + }); + + it("shows a validation error when showValidationErrors is true and no embedding model is set", () => { + renderWithProviders(); + expect(screen.getByText("An embedding model is required")).toBeInTheDocument(); + }); + + it("hides the validation error once an embedding model is set", () => { + renderWithProviders( + , + ); + expect(screen.queryByText("An embedding model is required")).not.toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.tsx b/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.tsx index 0f9907ac6c9..c7583427af6 100644 --- a/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.tsx +++ b/ui/litellm-dashboard/src/components/add_model/SemanticKeywordMatching.tsx @@ -15,6 +15,7 @@ interface SemanticKeywordMatchingProps { matchThreshold: number; onMatchThresholdChange: (threshold: number) => void; modelInfo: ModelGroup[]; + showValidationErrors?: boolean; } const SemanticKeywordMatching: React.FC = ({ @@ -25,11 +26,14 @@ const SemanticKeywordMatching: React.FC = ({ matchThreshold, onMatchThresholdChange, modelInfo, + showValidationErrors = false, }) => { - const modelOptions = Array.from(new Set(modelInfo.map((model) => model.model_group))).map((model_group) => ({ + const embeddingModels = modelInfo.filter((model) => model.mode === "embedding"); + const modelOptions = Array.from(new Set(embeddingModels.map((model) => model.model_group))).map((model_group) => ({ value: model_group, label: model_group, })); + const embeddingModelMissing = showValidationErrors && !embeddingModel; return ( @@ -60,7 +64,13 @@ const SemanticKeywordMatching: React.FC = ({ showSearch style={{ width: "100%" }} options={modelOptions} + status={embeddingModelMissing ? "error" : undefined} /> + {embeddingModelMissing && ( + + An embedding model is required + + )}
Minimum match score diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index 55c62224a48..ce69f8f7ae3 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -11,7 +11,11 @@ import RouterConfigBuilder from "./RouterConfigBuilder"; import ComplexityRouterConfig, { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; import { KeywordTierRule } from "./KeywordTierRules"; import { DEFAULT_MATCH_THRESHOLD } from "./SemanticKeywordMatching"; -import { buildComplexityRouterConfig, getSemanticConfigError } from "./build_complexity_router_config"; +import { + buildComplexityRouterConfig, + getMissingTiersError, + getSemanticConfigError, +} from "./build_complexity_router_config"; import { buildAutoRouterTestTargets, AutoRouterTestTarget } from "./build_auto_router_test_targets"; import AutoRouterConnectionTest from "./auto_router_connection_test"; import NotificationManager from "../molecules/notifications_manager"; @@ -43,6 +47,7 @@ const AddAutoRouterTab: React.FC = ({ form, handleOk, acc const [semanticMatchingEnabled, setSemanticMatchingEnabled] = useState(false); const [embeddingModel, setEmbeddingModel] = useState(undefined); const [matchThreshold, setMatchThreshold] = useState(DEFAULT_MATCH_THRESHOLD); + const [showValidationErrors, setShowValidationErrors] = useState(false); // Semantic router config (existing) const [routerConfig, setRouterConfig] = useState(null); @@ -86,19 +91,22 @@ const AddAutoRouterTab: React.FC = ({ form, handleOk, acc classifier_llm_config: classifierLlmConfig, } = complexityRouterConfig; - const filledTiers = Object.values(tiers).filter(Boolean); - if (filledTiers.length === 0) { - NotificationManager.fromBackend("Please select at least one model for a complexity tier"); + const missingTiersError = getMissingTiersError(tiers); + if (missingTiersError) { + setShowValidationErrors(true); + NotificationManager.fromBackend(missingTiersError); return; } if (classifierType === "llm" && !classifierLlmConfig?.model) { + setShowValidationErrors(true); NotificationManager.fromBackend("Please select a classifier model, or switch back to Heuristic"); return; } const semanticError = getSemanticConfigError({ semanticMatchingEnabled, embeddingModel, keywordTierRules }); if (semanticError) { + setShowValidationErrors(true); NotificationManager.fromBackend(semanticError); return; } @@ -296,6 +304,7 @@ const AddAutoRouterTab: React.FC = ({ form, handleOk, acc onEmbeddingModelChange={setEmbeddingModel} matchThreshold={matchThreshold} onMatchThresholdChange={setMatchThreshold} + showValidationErrors={showValidationErrors} />
) : ( diff --git a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts index 3c252646b57..e5b547d8240 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts @@ -1,5 +1,6 @@ import { buildComplexityRouterConfig, + getMissingTiersError, getSemanticConfigError, BuildComplexityRouterConfigParams, } from "./build_complexity_router_config"; @@ -124,6 +125,31 @@ describe("buildComplexityRouterConfig", () => { }); }); +describe("getMissingTiersError", () => { + it("returns null when all four tiers have a model", () => { + expect(getMissingTiersError(tiers)).toBeNull(); + }); + + it("names the specific missing tier when only one is blank", () => { + expect(getMissingTiersError({ ...tiers, REASONING: "" })).toBe( + "Select a model for the following tier(s): REASONING", + ); + }); + + it("names multiple missing tiers in SIMPLE/MEDIUM/COMPLEX/REASONING order", () => { + expect(getMissingTiersError({ ...tiers, SIMPLE: "", REASONING: "" })).toBe( + "Select a model for the following tier(s): SIMPLE, REASONING", + ); + }); + + it("names all four tiers when none are filled", () => { + const noTiers = { SIMPLE: "", MEDIUM: "", COMPLEX: "", REASONING: "" }; + expect(getMissingTiersError(noTiers)).toBe( + "Select a model for the following tier(s): SIMPLE, MEDIUM, COMPLEX, REASONING", + ); + }); +}); + describe("getSemanticConfigError", () => { const rule = { id: "r1", keywords: ["k8s"], tier: "REASONING" as const }; diff --git a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts index 82ea4f8c12f..3eddca8c35b 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts @@ -30,6 +30,14 @@ export interface ComplexityRouterConfigPayload { match_threshold?: number; } +const TIER_KEYS: Array = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]; + +export const getMissingTiersError = (tiers: ComplexityTiers): string | null => { + const missing = TIER_KEYS.filter((tier) => !tiers[tier]); + if (missing.length === 0) return null; + return `Select a model for the following tier(s): ${missing.join(", ")}`; +}; + export const getSemanticConfigError = ({ semanticMatchingEnabled, embeddingModel, From 2ed4ceb12e5ec5867d150a0d0eb5b9a97196cd4a Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:00:03 -0700 Subject: [PATCH 287/399] fix(model-cost-map): anchor the bedrock-claude-ids routing rule to the start of the id --- ...odel_prices_and_context_window_backup.json | 4 +-- model_prices_and_context_window.json | 4 +-- .../test_get_model_cost_map.py | 36 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index b4ae842c4e6..773ffb92b59 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45051,8 +45051,8 @@ "rules": [ { "name": "bedrock-claude-ids", - "pattern": "anthropic\\.claude-", - "description": "Any Bedrock-syntax Claude id: the dotted anthropic.claude- segment appears in bare (anthropic.claude-...), region-prefixed (us./eu./au./jp./apac.) and global.-prefixed ids, for every version. Routes these to bedrock before the bare-id Anthropic rule is consulted.", + "pattern": "^(?:[a-z-]+\\.)?anthropic\\.claude-", + "description": "A Bedrock-syntax Claude id, for every version: anthropic.claude- at the start of the name, optionally behind a single dotted geo segment (us./eu./au./jp./apac./global./us-gov.). Anchored to the start because routing rules see the raw request string and provider inference feeds the proxy's provider/* wildcard access checks: an id under an unrecognized namespace such as bedrockz/anthropic.claude-... must stay unroutable rather than resolve to bedrock and slip through a bedrock/* key. Routes to bedrock before the bare-id Anthropic rule is consulted.", "model_info": { "litellm_provider": "bedrock" } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 94d9f6496bd..6a770998331 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45284,8 +45284,8 @@ "rules": [ { "name": "bedrock-claude-ids", - "pattern": "anthropic\\.claude-", - "description": "Any Bedrock-syntax Claude id: the dotted anthropic.claude- segment appears in bare (anthropic.claude-...), region-prefixed (us./eu./au./jp./apac.) and global.-prefixed ids, for every version. Routes these to bedrock before the bare-id Anthropic rule is consulted.", + "pattern": "^(?:[a-z-]+\\.)?anthropic\\.claude-", + "description": "A Bedrock-syntax Claude id, for every version: anthropic.claude- at the start of the name, optionally behind a single dotted geo segment (us./eu./au./jp./apac./global./us-gov.). Anchored to the start because routing rules see the raw request string and provider inference feeds the proxy's provider/* wildcard access checks: an id under an unrecognized namespace such as bedrockz/anthropic.claude-... must stay unroutable rather than resolve to bedrock and slip through a bedrock/* key. Routes to bedrock before the bare-id Anthropic rule is consulted.", "model_info": { "litellm_provider": "bedrock" } diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index bdd71f28b1a..1a38b5dc769 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -138,6 +138,42 @@ def test_shipped_backup_carries_the_claude_routing_rules(): set_fallback_generalizations(previous) +def test_shipped_routing_rules_never_match_through_an_unrecognized_namespace(): + """Routing rules decide ``litellm_provider`` for otherwise-unknown ids, and the + proxy's wildcard access check (``can_key_call_model`` with a ``bedrock/*`` key) + trusts that inference: it rebuilds ``{provider}/{model}`` and matches it against + the key's patterns. A routing pattern that matches as a substring lets + ``bedrockz/anthropic.claude-...`` resolve to bedrock and slip through a + ``bedrock/*`` key, so every shipped routing rule must anchor to the start of + the name and never match an id carrying an unrecognized namespace prefix.""" + backup = GetModelCostMap.load_local_model_cost_map() + rules = backup[FALLBACK_GENERALIZATIONS_KEY]["rules"] + + routing_rules = [r for r in rules if "litellm_provider" in r["model_info"]] + assert routing_rules + assert all(r["pattern"].startswith("^") for r in routing_rules) + + previous = list(get_fallback_generalization_rules()) + try: + set_fallback_generalizations(rules) + for bedrock_id in [ + "anthropic.claude-3-5-sonnet-20240620-v1:0", + "anthropic.claude-v2:1", + "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0", + "global.anthropic.claude-fable-5-20260120-v1:0", + ]: + assert match_routing_generalization(bedrock_id) == "bedrock", bedrock_id + for namespaced in [ + "bedrockz/anthropic.claude-3-5-sonnet-20240620", + "bedrockz/us.anthropic.claude-3-5-sonnet-20240620-v1:0", + "bedrockz/claude-3-5-sonnet-20240620", + ]: + assert match_routing_generalization(namespaced) is None, namespaced + finally: + set_fallback_generalizations(previous) + + def test_shipped_backup_marks_claude_4_6_plus_adaptive_not_4_0(): """Adaptive thinking is data, not code. The bundled backup must carry supports_adaptive_thinking on genuine Claude >= 4.6 entries (every provider From d0d1c0e346fdb5f907666b1fb4aa0b26f21beb9c Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:00:03 -0700 Subject: [PATCH 288/399] fix(proxy-auth): deny provider-wildcard access inferred through an unrecognized model namespace --- litellm/proxy/auth/auth_checks.py | 13 +++++++++++-- tests/proxy_unit_tests/test_auth_checks.py | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 230b9b70ff0..93811812901 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -4383,14 +4383,23 @@ def _model_custom_llm_provider_matches_wildcard_pattern(model: str, allowed_mode or - `model=claude-3-5-sonnet-20240620` - `allowed_model_pattern=anthropic/*` + + A model that already carries a namespace get_llm_provider did not consume + (e.g. `bedrockz/anthropic.claude-...`) is never granted here: its provider was + inferred from a fragment of the full string, so rebuilding + `{provider}/{model}` would produce `bedrock/bedrockz/...` and slip an + unrecognized namespace through a `bedrock/*` key. """ try: - model, custom_llm_provider, _, _ = get_llm_provider(model=model) + stripped_model, custom_llm_provider, _, _ = get_llm_provider(model=model) except Exception: return False + if stripped_model == model and "/" in model: + return False + return is_model_allowed_by_pattern( - model=f"{custom_llm_provider}/{model}", + model=f"{custom_llm_provider}/{stripped_model}", allowed_model_pattern=allowed_model_pattern, ) diff --git a/tests/proxy_unit_tests/test_auth_checks.py b/tests/proxy_unit_tests/test_auth_checks.py index e7136ecb195..e58e6c9694b 100644 --- a/tests/proxy_unit_tests/test_auth_checks.py +++ b/tests/proxy_unit_tests/test_auth_checks.py @@ -236,6 +236,8 @@ async def test_can_team_call_model(model, expect_to_work): (["bedrock/*"], "bedrock/anthropic.claude-3-5-sonnet-20240620", True), (["bedrock/*"], "bedrockz/anthropic.claude-3-5-sonnet-20240620", False), (["bedrock/us.*"], "bedrock/us.amazon.nova-micro-v1:0", True), + (["openai/*"], "ft:gpt-4-0613", True), + (["openai/*"], "bedrockz/ft:gpt-4-0613", False), ], ) @pytest.mark.asyncio From 3464b5e7dfaf027e366f95f60c55e89265d6601e Mon Sep 17 00:00:00 2001 From: Abhimanyu Kapur <38531241+akapur99@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:03:48 -0700 Subject: [PATCH 289/399] fix(auto_router): reset inline validation errors when switching router type --- .../src/components/add_model/add_auto_router_tab.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index ce69f8f7ae3..4e7a72435bf 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -238,7 +238,14 @@ const AddAutoRouterTab: React.FC = ({ form, handleOk, acc
Router Type - setRouterType(e.target.value)} className="w-full"> + { + setRouterType(e.target.value); + setShowValidationErrors(false); + }} + className="w-full" + >
From d6883d15b0feac1bfe07eaa18414ef14b1a5a7f4 Mon Sep 17 00:00:00 2001 From: Abhimanyu Kapur <38531241+akapur99@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:08:07 -0700 Subject: [PATCH 290/399] fix(auto_router): flag name field and tier fields together on empty submit Clicking Add Auto Router with the name empty returned early with only a toast, so blank tier selects never got their inline error state. The empty-name branch now sets showValidationErrors and triggers antd validation on the name field, so every unfilled mandatory field is flagged at once. Adds a regression test for the tab component. --- .../add_model/add_auto_router_tab.test.tsx | 40 +++++++++++++++++++ .../add_model/add_auto_router_tab.tsx | 2 + 2 files changed, 42 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx new file mode 100644 index 00000000000..4713f8c6869 --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx @@ -0,0 +1,40 @@ +import { renderWithProviders, screen } from "../../../tests/test-utils"; +import userEvent from "@testing-library/user-event"; +import { vi } from "vitest"; +import { Form } from "antd"; +import AddAutoRouterTab from "./add_auto_router_tab"; +import NotificationManager from "../molecules/notifications_manager"; + +vi.mock("../networking", () => ({ + modelAvailableCall: vi.fn().mockResolvedValue({ data: [] }), +})); + +vi.mock("@/components/llm_calls/fetch_models", () => ({ + fetchAvailableModels: vi.fn().mockResolvedValue([]), +})); + +vi.mock("./handle_add_auto_router_submit", () => ({ + handleAddAutoRouterSubmit: vi.fn(), +})); + +vi.mock("../molecules/notifications_manager", () => ({ + default: { fromBackend: vi.fn() }, +})); + +const Harness = () => { + const [form] = Form.useForm(); + return ; +}; + +describe("AddAutoRouterTab", () => { + it("flags every mandatory field when Add Auto Router is clicked with nothing filled", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + await user.click(screen.getByRole("button", { name: /add auto router/i })); + + expect(await screen.findByText("Auto router name is required")).toBeInTheDocument(); + expect(screen.getAllByText("This tier is required")).toHaveLength(4); + expect(NotificationManager.fromBackend).toHaveBeenCalledWith("Please enter an Auto Router Name"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index 4e7a72435bf..a74eab0abdd 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -198,6 +198,8 @@ const AddAutoRouterTab: React.FC = ({ form, handleOk, acc const handleAutoRouterSubmit = () => { const name = form.getFieldValue("auto_router_name"); if (!name) { + setShowValidationErrors(true); + form.validateFields(["auto_router_name"]).catch(() => undefined); NotificationManager.fromBackend("Please enter an Auto Router Name"); return; } From 34c6cce70562ded634b27771a11f698c46c184dd Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Fri, 10 Jul 2026 14:36:20 -0700 Subject: [PATCH 291/399] feat(mcp): mint gateway-bound envelope at the token endpoint for dcr_bridge oauth_delegate --- .../mcp_server/discoverable_endpoints.py | 91 +++++++++++++- .../mcp_server/test_discoverable_endpoints.py | 116 ++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index ebceb320906..41ed49a7508 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -10,7 +10,7 @@ from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse import httpx from fastapi import APIRouter, Form, HTTPException, Request from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response -from pydantic import BaseModel, ValidationError +from pydantic import BaseModel, SecretStr, ValidationError from litellm._logging import verbose_logger from litellm.llms.custom_httpx.http_handler import ( @@ -37,6 +37,9 @@ from litellm.types.mcp import MCPAuth, MCPCredentials from litellm.types.mcp_server.mcp_server_manager import MCPServer if TYPE_CHECKING: + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + UpstreamTokenGrant, + ) from litellm.proxy._types import LiteLLM_MCPServerTable, UserAPIKeyAuth # TTL cache for upstream OAuth metadata fetched from pass-through MCP servers. @@ -654,6 +657,86 @@ async def authorize_with_server( return response +def _bridge_grant_from_token_response(token_response: object) -> Optional["UpstreamTokenGrant"]: + """Validate an upstream OAuth token response into a typed grant, or None when it lacks a usable + access token. Each field is isinstance-checked so nothing untyped from ``response.json()`` flows + into the grant.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import + UpstreamTokenGrant, + ) + + if not isinstance(token_response, dict): + return None + access = token_response.get("access_token") + if not isinstance(access, str) or not access: + return None + token_type = token_response.get("token_type") + refresh = token_response.get("refresh_token") + scope = token_response.get("scope") + expires_in = token_response.get("expires_in") + return UpstreamTokenGrant( + access_token=SecretStr(access), + token_type=token_type if isinstance(token_type, str) and token_type else "Bearer", + refresh_token=SecretStr(refresh) if isinstance(refresh, str) and refresh else None, + scope=scope if isinstance(scope, str) and scope else None, + expires_in=expires_in if isinstance(expires_in, int) and expires_in > 0 else None, + ) + + +async def _mint_bridge_delegate_token_response( + request: Request, mcp_server: MCPServer, token_response: object +) -> JSONResponse: + """Return the client-held envelope bearer for a DCR-bridge ``oauth_delegate`` token exchange. + + The envelope binds the caller's litellm identity (resolved from the token request) to the + upstream grant, so the client holds one bearer that later admits it and forwards the upstream + token, with nothing stored server-side. Fails closed with an OAuth ``invalid_request`` when no + litellm identity accompanies the token request rather than minting an identity-less credential. + """ + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import + build_bridge_token_response, + envelope_keys_from_master_key, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import + EnvelopeIdentity, + SealedEnvelope, + ) + from litellm.proxy.proxy_server import ( + master_key, # noqa: PLC0415 # inline import avoids a module-load circular import + ) + + if not master_key: + raise HTTPException(status_code=500, detail="Server misconfigured: master_key is not set") + + user_id = await _extract_user_id_from_request(request) + if not user_id: + raise HTTPException( + status_code=400, + detail={ + "error": "invalid_request", + "error_description": ( + "this server issues a gateway-bound credential; send a litellm credential " + "(x-litellm-api-key or Authorization) on the token request" + ), + }, + ) + + grant = _bridge_grant_from_token_response(token_response) + if grant is None: + raise HTTPException(status_code=502, detail="Upstream token response has no usable access_token") + + now = datetime.now(timezone.utc) + keys = envelope_keys_from_master_key(master_key) + identity = EnvelopeIdentity(user_id=user_id, server_id=mcp_server.server_id) + sealed = build_bridge_token_response(identity, grant, keys, now) + if not isinstance(sealed, SealedEnvelope): + raise HTTPException(status_code=500, detail="Failed to mint the gateway-bound credential") + + expires_in = max(1, int((sealed.expires_at - now).total_seconds())) + body = {"access_token": sealed.token.get_secret_value(), "token_type": "Bearer", "expires_in": expires_in} + return JSONResponse(body, headers=TOKEN_NO_CACHE_HEADERS) + + async def exchange_token_with_server( request: Request, mcp_server: MCPServer, @@ -791,6 +874,12 @@ async def exchange_token_with_server( mcp_server.server_id, ) + # A DCR-bridge oauth_delegate server hands the client a gateway-bound envelope (identity plus the + # upstream token) instead of the raw upstream token, so the one bearer both admits the caller and + # forwards the upstream credential. Only this mode mints; every other server returns the raw token. + if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: + return await _mint_bridge_delegate_token_response(request, mcp_server, token_response) + result = { "access_token": access_token, "token_type": token_response.get("token_type", "Bearer"), diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index c55a631c7b3..a2e8d693fab 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4362,6 +4362,122 @@ async def test_register_bridge_relay_never_persists(): mock_persist.assert_not_called() +_BRIDGE_MASTER_KEY = "sk-bridge-producer-master-key-0123456789abcdef" + + +async def _exchange_for_bridge_server(server, upstream_body, user_id): + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + + fake_http_response = MagicMock() + fake_http_response.json.return_value = upstream_body + fake_http_response.raise_for_status = MagicMock() + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock(return_value=fake_http_response) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_user_id_from_request", + new=AsyncMock(return_value=user_id), + ), + patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), + ): + return await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri="https://claude.ai/api/mcp/auth_callback", + client_id="dcr-client-123", + client_secret=None, + code_verifier="verifier", + ) + + +@pytest.mark.asyncio +async def test_oauth_delegate_bridge_token_exchange_mints_envelope_not_raw_token(): + """A dcr_bridge oauth_delegate token exchange returns a gateway-bound envelope, not the raw + upstream token: the response access_token opens (under the same master-key-derived keys and the + server_id) to the caller's identity and the upstream Authorization, and the raw upstream token + never appears in the bearer the client receives.""" + from datetime import datetime, timezone + + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + BridgeEnvelopeAdmitted, + envelope_keys_from_master_key, + resolve_bridge_envelope, + ) + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + response = await _exchange_for_bridge_server(server, upstream, user_id="user-77") + + body = json.loads(response.body) + token = body["access_token"] + assert body["token_type"] == "Bearer" + assert body["expires_in"] > 0 + assert token.startswith("llm_env_") + assert "UPSTREAM-SECRET-TOKEN" not in token + assert "refresh_token" not in body + + keys = envelope_keys_from_master_key(_BRIDGE_MASTER_KEY) + opened = resolve_bridge_envelope(token, keys, datetime.now(timezone.utc), server.server_id) + assert isinstance(opened, BridgeEnvelopeAdmitted) + assert opened.identity.user_id == "user-77" + assert opened.upstream_authorization.get_secret_value() == "Bearer UPSTREAM-SECRET-TOKEN" + + +@pytest.mark.asyncio +async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm_identity(): + """Without a resolvable litellm identity on the token request, the exchange must not mint an + identity-less envelope; it returns an OAuth invalid_request so the client sends a credential.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + + with pytest.raises(HTTPException) as exc: + await _exchange_for_bridge_server(server, upstream, user_id=None) + + assert exc.value.status_code == 400 + assert exc.value.detail["error"] == "invalid_request" + + +@pytest.mark.asyncio +async def test_true_passthrough_bridge_token_exchange_returns_raw_upstream_token(): + """Only oauth_delegate mints. A true_passthrough dcr_bridge server relays the raw upstream token + to the client, since that mode has no litellm identity to bind and the caller owns the token.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.true_passthrough) + upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + response = await _exchange_for_bridge_server(server, upstream, user_id="user-77") + + body = json.loads(response.body) + assert body["access_token"] == "UPSTREAM-SECRET-TOKEN" + assert not body["access_token"].startswith("llm_env_") + + +@pytest.mark.asyncio +async def test_non_bridge_oauth_delegate_token_exchange_returns_raw_upstream_token(): + """An oauth_delegate server without dcr_bridge keeps the pre-change contract: the raw upstream + token is returned, so flag-off behavior is byte-identical.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate, dcr_bridge=None) + upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + response = await _exchange_for_bridge_server(server, upstream, user_id="user-77") + + body = json.loads(response.body) + assert body["access_token"] == "UPSTREAM-SECRET-TOKEN" + + async def _exchange_persistence_attempted_for_auth_type(auth_type) -> bool: """Run exchange_token_with_server for a server of ``auth_type`` and report whether it attempted to persist the exchanged token server-side. The client-forwarded token modes must not persist: From 85255c96fb45fc2473fa5b4ad1939102c8ab9db0 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Fri, 10 Jul 2026 17:22:59 -0700 Subject: [PATCH 292/399] feat(mcp): seal the authorizing key hash in the dcr_bridge envelope The mint bound only user_id/server_id into the envelope, which gave admission no way to reload the caller's key and enforce its current restrictions. Seal the hashed authorizing key instead (a one-way digest, not a usable credential), so admission reloads the live UserAPIKeyAuth by it and the key's team/org/tool permissions and revocation apply per request. Extract the token endpoint's key resolution into a shared _resolve_active_litellm_key so the per-user token store (user_id) and the bridge mint (key hash) derive from one active-key-gated path, and fail the mint closed with invalid_request when no active key accompanies the request. --- .../mcp_server/discoverable_endpoints.py | 83 +++++++++++++------ .../mcp_server/test_discoverable_endpoints.py | 78 +++++++++++++++-- 2 files changed, 127 insertions(+), 34 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 41ed49a7508..eeadeb290f3 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -351,44 +351,73 @@ def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> Optional[str]: return key_obj.user_id -async def _extract_user_id_from_request(request: Request) -> Optional[str]: - """Resolve the LiteLLM ``user_id`` at the OAuth token endpoint so a per-user token is stored - under the same identity the egress later reads it by (``user_api_key_auth.user_id``). +async def _resolve_active_litellm_key(request: Request) -> Optional[Tuple[str, "UserAPIKeyAuth"]]: + """Resolve the presented litellm key to ``(its hash, the live active key record)``, or ``None`` + when the key is absent, unresolvable, or blocked/expired. - Resolves authoritatively via ``get_key_object`` (cache first, then DB) instead of a raw cache - peek. On a multi-replica gateway the token-exchange request can land on a worker whose in-memory - cache never saw the key, and a cross-replica Redis hit deserializes to a plain ``dict`` rather - than a ``UserAPIKeyAuth``; the previous code read only ``Authorization`` and did - ``getattr(cached, "user_id")`` with no ``model_type`` rehydration and no DB fallback, so it - silently returned ``None`` and the token was never persisted, which makes the egress 401 on every - reconnect. The resolved key is validated (``_active_key_user_id``) before its identity is trusted, - so a blocked or expired key cannot write. Returns ``None`` when no key is present, the key cannot - be resolved, or it is blocked/expired. + Single resolution path the OAuth token endpoint reuses. Resolves authoritatively via + ``get_key_object`` (cache first, then DB) instead of a raw cache peek. On a multi-replica gateway + the token-exchange request can land on a worker whose in-memory cache never saw the key, and a + cross-replica Redis hit deserializes to a plain ``dict`` rather than a ``UserAPIKeyAuth``; the + previous code read only ``Authorization`` and did ``getattr(cached, "user_id")`` with no + ``model_type`` rehydration and no DB fallback, so it silently returned ``None``. The resolved key + is validated (``_active_key_user_id``) before it is trusted, so a blocked or expired key resolves + to ``None``. The returned hash is the value ``get_key_object`` and the cache/DB layer key the + record by. Callers derive the ``user_id`` (per-user token store) or seal the hash (dcr_bridge + envelope) from the result. """ token = _litellm_key_from_request(request) if not token: return None try: - from litellm.proxy._types import hash_token # noqa: PLC0415 - from litellm.proxy.auth.auth_checks import get_key_object # noqa: PLC0415 - from litellm.proxy.proxy_server import ( # noqa: PLC0415 + from litellm.proxy._types import ( # noqa: PLC0415 # inline import avoids a module-load circular import + hash_token, + ) + from litellm.proxy.auth.auth_checks import ( # noqa: PLC0415 # inline import avoids a module-load circular import + get_key_object, + ) + from litellm.proxy.proxy_server import ( # noqa: PLC0415 # inline import avoids a module-load circular import prisma_client, user_api_key_cache, ) + key_hash = hash_token(token) key_obj = await get_key_object( - hashed_token=hash_token(token), + hashed_token=key_hash, prisma_client=prisma_client, user_api_key_cache=user_api_key_cache, ) - return _active_key_user_id(key_obj) - except Exception as exc: + except Exception as exc: # noqa: BLE001 # fail closed to None on any key-resolution error verbose_logger.debug( - "_extract_user_id_from_request: could not resolve a LiteLLM user_id for the presented " - "key (%s); per-user token will not be stored server-side.", + "_resolve_active_litellm_key: could not resolve the presented key (%s)", type(exc).__name__, ) return None + if _active_key_user_id(key_obj) is None: + return None + return key_hash, key_obj + + +async def _extract_user_id_from_request(request: Request) -> Optional[str]: + """The LiteLLM ``user_id`` for the token request, so a per-user token is stored under the same + identity the egress later reads it by (``user_api_key_auth.user_id``). ``None`` when no active + key is present. See :func:`_resolve_active_litellm_key` for the resolution and active-key gate. + """ + resolved = await _resolve_active_litellm_key(request) + return _active_key_user_id(resolved[1]) if resolved else None + + +async def _extract_active_key_hash_from_request(request: Request) -> Optional[str]: + """The hash of the litellm key that authorized the token request, when it maps to an active key. + + A DCR-bridge envelope seals this hash so admission can reload the live ``UserAPIKeyAuth`` record + and enforce the key's current team/org/tool restrictions and revocation, rather than trusting a + frozen identity. The hash is a one-way digest, not a usable credential (the edge rejects a bare + hash presented as a bearer). ``None`` when no active key is present, so no envelope is minted for + a missing, unresolvable, or revoked key. + """ + resolved = await _resolve_active_litellm_key(request) + return resolved[0] if resolved else None async def _store_per_user_token_server_side( @@ -688,10 +717,12 @@ async def _mint_bridge_delegate_token_response( ) -> JSONResponse: """Return the client-held envelope bearer for a DCR-bridge ``oauth_delegate`` token exchange. - The envelope binds the caller's litellm identity (resolved from the token request) to the + The envelope binds the authorizing litellm key (its hash, resolved from the token request) to the upstream grant, so the client holds one bearer that later admits it and forwards the upstream - token, with nothing stored server-side. Fails closed with an OAuth ``invalid_request`` when no - litellm identity accompanies the token request rather than minting an identity-less credential. + token, with nothing stored server-side. Admission reloads the live key by that hash, so the key's + current restrictions and revocation gate the request. Fails closed with an OAuth + ``invalid_request`` when no active litellm key accompanies the token request rather than minting + an unbound credential. """ from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import build_bridge_token_response, @@ -708,8 +739,8 @@ async def _mint_bridge_delegate_token_response( if not master_key: raise HTTPException(status_code=500, detail="Server misconfigured: master_key is not set") - user_id = await _extract_user_id_from_request(request) - if not user_id: + key_hash = await _extract_active_key_hash_from_request(request) + if not key_hash: raise HTTPException( status_code=400, detail={ @@ -727,7 +758,7 @@ async def _mint_bridge_delegate_token_response( now = datetime.now(timezone.utc) keys = envelope_keys_from_master_key(master_key) - identity = EnvelopeIdentity(user_id=user_id, server_id=mcp_server.server_id) + identity = EnvelopeIdentity(server_id=mcp_server.server_id, key_hash=key_hash) sealed = build_bridge_token_response(identity, grant, keys, now) if not isinstance(sealed, SealedEnvelope): raise HTTPException(status_code=500, detail="Failed to mint the gateway-bound credential") diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index a2e8d693fab..e69d94d9615 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4365,7 +4365,7 @@ async def test_register_bridge_relay_never_persists(): _BRIDGE_MASTER_KEY = "sk-bridge-producer-master-key-0123456789abcdef" -async def _exchange_for_bridge_server(server, upstream_body, user_id): +async def _exchange_for_bridge_server(server, upstream_body, key_hash): from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( exchange_token_with_server, ) @@ -4382,8 +4382,8 @@ async def _exchange_for_bridge_server(server, upstream_body, user_id): return_value=fake_http_client, ), patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_user_id_from_request", - new=AsyncMock(return_value=user_id), + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", + new=AsyncMock(return_value=key_hash), ), patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), ): @@ -4416,7 +4416,7 @@ async def test_oauth_delegate_bridge_token_exchange_mints_envelope_not_raw_token server = _bridge_server(auth_type=MCPAuth.oauth_delegate) upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} - response = await _exchange_for_bridge_server(server, upstream, user_id="user-77") + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") body = json.loads(response.body) token = body["access_token"] @@ -4429,7 +4429,7 @@ async def test_oauth_delegate_bridge_token_exchange_mints_envelope_not_raw_token keys = envelope_keys_from_master_key(_BRIDGE_MASTER_KEY) opened = resolve_bridge_envelope(token, keys, datetime.now(timezone.utc), server.server_id) assert isinstance(opened, BridgeEnvelopeAdmitted) - assert opened.identity.user_id == "user-77" + assert opened.identity.key_hash == "hashed-litellm-key-77" assert opened.upstream_authorization.get_secret_value() == "Bearer UPSTREAM-SECRET-TOKEN" @@ -4443,7 +4443,7 @@ async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} with pytest.raises(HTTPException) as exc: - await _exchange_for_bridge_server(server, upstream, user_id=None) + await _exchange_for_bridge_server(server, upstream, key_hash=None) assert exc.value.status_code == 400 assert exc.value.detail["error"] == "invalid_request" @@ -4457,7 +4457,7 @@ async def test_true_passthrough_bridge_token_exchange_returns_raw_upstream_token server = _bridge_server(auth_type=MCPAuth.true_passthrough) upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} - response = await _exchange_for_bridge_server(server, upstream, user_id="user-77") + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") body = json.loads(response.body) assert body["access_token"] == "UPSTREAM-SECRET-TOKEN" @@ -4472,7 +4472,7 @@ async def test_non_bridge_oauth_delegate_token_exchange_returns_raw_upstream_tok server = _bridge_server(auth_type=MCPAuth.oauth_delegate, dcr_bridge=None) upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} - response = await _exchange_for_bridge_server(server, upstream, user_id="user-77") + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") body = json.loads(response.body) assert body["access_token"] == "UPSTREAM-SECRET-TOKEN" @@ -4822,6 +4822,68 @@ async def test_extract_user_id_rejects_expired_key(proxy_globals): assert await _extract_user_id_from_request(request) is None +@pytest.mark.asyncio +async def test_extract_active_key_hash_returns_hash_for_active_key(proxy_globals): + """The dcr_bridge mint seals the hash of the authorizing key so admission can reload the live + record. For an active key the resolver returns exactly hash_token(key), the same value + get_key_object and the whole cache/DB layer key the record by, so the sealed reference resolves + back to this key at admission.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _extract_active_key_hash_from_request, + ) + from litellm.proxy._types import UserAPIKeyAuth, hash_token + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + key = "sk-alice-key" + cache = UserApiKeyCache() + await cache.async_set_cache( + hash_token(key), + UserAPIKeyAuth(token=hash_token(key), user_id="alice"), + model_type=UserAPIKeyAuth, + ) + proxy_globals.user_api_key_cache = cache + proxy_globals.prisma_client = object() + + request = _token_request({"x-litellm-api-key": f"Bearer {key}"}) + assert await _extract_active_key_hash_from_request(request) == hash_token(key) + + +@pytest.mark.asyncio +async def test_extract_active_key_hash_rejects_blocked_key(proxy_globals): + """A blocked key must not yield a hash, so no gateway-bound envelope is minted for a revoked key; + the mint fails closed with invalid_request instead.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _extract_active_key_hash_from_request, + ) + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + class _FakePrisma: + async def get_data(self, token, table_name, parent_otel_span=None, proxy_logging_obj=None): + return UserAPIKeyAuth(token=token, user_id="blocked-user", blocked=True) + + proxy_globals.user_api_key_cache = UserApiKeyCache() + proxy_globals.prisma_client = _FakePrisma() + + request = _token_request({"x-litellm-api-key": "sk-blocked-key"}) + assert await _extract_active_key_hash_from_request(request) is None + + +@pytest.mark.asyncio +async def test_extract_active_key_hash_none_without_litellm_key(proxy_globals): + """No LiteLLM key on the request yields no hash without consulting the resolver.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _extract_active_key_hash_from_request, + ) + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + proxy_globals.user_api_key_cache = UserApiKeyCache() + proxy_globals.prisma_client = object() + + request = _token_request({"content-type": "application/json"}) + assert await _extract_active_key_hash_from_request(request) is None + + @pytest.mark.asyncio async def test_token_endpoint_uses_client_secret_basic_when_configured(): """LIT-4091: a server with token_endpoint_auth_method=client_secret_basic must send the From 7df848aa6c6efe2fd32ea9174cd2e5bc51ed479f Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 14:41:44 -0700 Subject: [PATCH 293/399] fix(mcp): return 502 not KeyError when a bridge upstream response lacks access_token The eager access_token = token_response["access_token"] extraction ran before the dcr_bridge branch, so a missing upstream access_token raised an unhandled KeyError and _bridge_grant_from_token_response's nil guard (which maps to a clean 502) was dead code. Move the extraction onto the non-bridge result path so the bridge branch reaches its 502 guard. --- .../mcp_server/discoverable_endpoints.py | 3 +-- .../mcp_server/test_discoverable_endpoints.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index eeadeb290f3..ef42fef312d 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -865,7 +865,6 @@ async def exchange_token_with_server( ) raise token_response = response.json() - access_token = token_response["access_token"] # Validate token response against server-configured rules before any storage. # This rejects tokens from wrong Slack workspaces, Atlassian orgs, etc. @@ -912,7 +911,7 @@ async def exchange_token_with_server( return await _mint_bridge_delegate_token_response(request, mcp_server, token_response) result = { - "access_token": access_token, + "access_token": token_response["access_token"], "token_type": token_response.get("token_type", "Bearer"), } diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index e69d94d9615..5826d1f28b6 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4449,6 +4449,23 @@ async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm assert exc.value.detail["error"] == "invalid_request" +@pytest.mark.asyncio +async def test_oauth_delegate_bridge_token_exchange_missing_access_token_is_502_not_keyerror(): + """When the upstream token response has no access_token, a dcr_bridge oauth_delegate exchange + returns a clean 502 rather than raising a KeyError. The eager access_token extraction used to run + before the bridge branch, so a missing token raised KeyError and _bridge_grant_from_token_response's + nil guard (which maps to 502) was dead code; the extraction now lives on the non-bridge path only.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"token_type": "Bearer", "expires_in": 3600} + + with pytest.raises(HTTPException) as exc: + await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + + assert exc.value.status_code == 502 + + @pytest.mark.asyncio async def test_true_passthrough_bridge_token_exchange_returns_raw_upstream_token(): """Only oauth_delegate mints. A true_passthrough dcr_bridge server relays the raw upstream token From 362c78e30864f5826a3dc826dc5428c84a7d4f5e Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 15:05:30 -0700 Subject: [PATCH 294/399] fix(mcp): let a keyless-user active key mint a bridge envelope _resolve_active_litellm_key gated on _active_key_user_id, which returns None both for blocked/expired keys AND for valid keys with no user_id, so a team-scoped or service-account key was wrongly rejected with invalid_request at bridge token exchange. Split the active-state gate (_key_is_active: blocked/expiry only) from the user_id extraction; the mint seals the key hash, not the user, and admission already handles a keyless-user key. The per-user token store still gets no user for such a key, as there is none to key a stored credential by. --- .../mcp_server/discoverable_endpoints.py | 44 ++++++++++++------- .../mcp_server/test_discoverable_endpoints.py | 29 ++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index ef42fef312d..df1615aff99 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -329,26 +329,37 @@ def _litellm_key_from_request(request: Request) -> Optional[str]: return None -def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> Optional[str]: - """The key's ``user_id``, or ``None`` if the key is blocked or expired. +def _key_is_active(key_obj: "UserAPIKeyAuth") -> bool: + """``True`` when the presented key is neither blocked nor past its expiry. - The OAuth token endpoint is unauthenticated, so the presented key is validated here before its - identity is trusted to key a stored credential; a revoked or expired key must not be able to - write or overwrite the per-user OAuth token. ``get_key_object`` resolves a row without these - checks (the main ``user_api_key_auth`` pipeline enforces them downstream, which this endpoint - bypasses), so they are applied here. Deleted keys are already rejected upstream, where - ``get_key_object`` raises on a row that no longer exists. + The OAuth token endpoint is unauthenticated, so the presented key is validated here before it is + trusted; a revoked or expired key must not mint a bridge envelope or write a stored credential. + ``get_key_object`` resolves a row without these checks (the main ``user_api_key_auth`` pipeline + enforces them downstream, which this endpoint bypasses), so they are applied here. Deleted keys + are already rejected upstream, where ``get_key_object`` raises on a row that no longer exists. + + This is an active-state gate only; it deliberately does not require a ``user_id``. A valid + team-scoped or service-account key has no ``user_id`` yet is a legitimate credential, so gating + on ``user_id`` presence would wrongly reject it. Callers that need the user (the per-user token + store) derive it separately via :func:`_active_key_user_id`. """ if key_obj.blocked is True: - return None + return False expires = key_obj.expires if expires is not None: expiry = expires if isinstance(expires, datetime) else datetime.fromisoformat(expires) if expiry.tzinfo is None or expiry.tzinfo.utcoffset(expiry) is None: expiry = expiry.replace(tzinfo=timezone.utc) if expiry < datetime.now(timezone.utc): - return None - return key_obj.user_id + return False + return True + + +def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> Optional[str]: + """The active key's ``user_id``, or ``None`` when the key is blocked/expired or simply has no + ``user_id`` (a team-scoped or service-account key). Used only by the per-user token store, which + needs a user to key the stored credential; the bridge mint uses the key hash and does not.""" + return key_obj.user_id if _key_is_active(key_obj) else None async def _resolve_active_litellm_key(request: Request) -> Optional[Tuple[str, "UserAPIKeyAuth"]]: @@ -361,10 +372,11 @@ async def _resolve_active_litellm_key(request: Request) -> Optional[Tuple[str, " cross-replica Redis hit deserializes to a plain ``dict`` rather than a ``UserAPIKeyAuth``; the previous code read only ``Authorization`` and did ``getattr(cached, "user_id")`` with no ``model_type`` rehydration and no DB fallback, so it silently returned ``None``. The resolved key - is validated (``_active_key_user_id``) before it is trusted, so a blocked or expired key resolves - to ``None``. The returned hash is the value ``get_key_object`` and the cache/DB layer key the - record by. Callers derive the ``user_id`` (per-user token store) or seal the hash (dcr_bridge - envelope) from the result. + is validated (``_key_is_active``) before it is trusted, so a blocked or expired key resolves to + ``None``, while a valid team-scoped or service-account key (no ``user_id``) still resolves so it + can mint a bridge envelope. The returned hash is the value ``get_key_object`` and the cache/DB + layer key the record by. Callers derive the ``user_id`` (per-user token store) or seal the hash + (dcr_bridge envelope) from the result. """ token = _litellm_key_from_request(request) if not token: @@ -393,7 +405,7 @@ async def _resolve_active_litellm_key(request: Request) -> Optional[Tuple[str, " type(exc).__name__, ) return None - if _active_key_user_id(key_obj) is None: + if not _key_is_active(key_obj): return None return key_hash, key_obj diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 5826d1f28b6..2bf5e49ec79 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4865,6 +4865,35 @@ async def test_extract_active_key_hash_returns_hash_for_active_key(proxy_globals assert await _extract_active_key_hash_from_request(request) == hash_token(key) +@pytest.mark.asyncio +async def test_extract_active_key_hash_returns_hash_for_active_key_without_user_id(proxy_globals): + """A valid team-scoped or service-account key has no user_id but is a legitimate credential, so it + must still resolve to a hash and be able to mint a bridge envelope. Gating the resolver on user_id + presence wrongly rejected these keys with invalid_request; the active-state gate now checks only + blocked and expiry, and the key hash (not the user) is what the mint seals. The per-user token + store still gets no user for such a key, since there is none to key a stored credential by.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _extract_active_key_hash_from_request, + _extract_user_id_from_request, + ) + from litellm.proxy._types import UserAPIKeyAuth, hash_token + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + key = "sk-team-scoped-key" + cache = UserApiKeyCache() + await cache.async_set_cache( + hash_token(key), + UserAPIKeyAuth(token=hash_token(key), user_id=None, team_id="team-x"), + model_type=UserAPIKeyAuth, + ) + proxy_globals.user_api_key_cache = cache + proxy_globals.prisma_client = object() + + request = _token_request({"x-litellm-api-key": f"Bearer {key}"}) + assert await _extract_active_key_hash_from_request(request) == hash_token(key) + assert await _extract_user_id_from_request(request) is None + + @pytest.mark.asyncio async def test_extract_active_key_hash_rejects_blocked_key(proxy_globals): """A blocked key must not yield a hash, so no gateway-bound envelope is minted for a revoked key; From ceff2d1f3c99f2e314e497e86c302372ba26e64b Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 15:47:27 -0700 Subject: [PATCH 295/399] style(mcp): use X | None annotations on the touched key-resolution helpers The keyless-user fix moved these signatures, so their pre-existing Optional[...] annotations counted against the diff and tripped the UP045 strict-budget gate. Modernize the four touched return annotations to the X | None form the gate wants; runtime behavior is unchanged. --- .../_experimental/mcp_server/discoverable_endpoints.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index df1615aff99..c5d7c3b40fd 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -355,14 +355,14 @@ def _key_is_active(key_obj: "UserAPIKeyAuth") -> bool: return True -def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> Optional[str]: +def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> str | None: """The active key's ``user_id``, or ``None`` when the key is blocked/expired or simply has no ``user_id`` (a team-scoped or service-account key). Used only by the per-user token store, which needs a user to key the stored credential; the bridge mint uses the key hash and does not.""" return key_obj.user_id if _key_is_active(key_obj) else None -async def _resolve_active_litellm_key(request: Request) -> Optional[Tuple[str, "UserAPIKeyAuth"]]: +async def _resolve_active_litellm_key(request: Request) -> Tuple[str, "UserAPIKeyAuth"] | None: """Resolve the presented litellm key to ``(its hash, the live active key record)``, or ``None`` when the key is absent, unresolvable, or blocked/expired. @@ -410,7 +410,7 @@ async def _resolve_active_litellm_key(request: Request) -> Optional[Tuple[str, " return key_hash, key_obj -async def _extract_user_id_from_request(request: Request) -> Optional[str]: +async def _extract_user_id_from_request(request: Request) -> str | None: """The LiteLLM ``user_id`` for the token request, so a per-user token is stored under the same identity the egress later reads it by (``user_api_key_auth.user_id``). ``None`` when no active key is present. See :func:`_resolve_active_litellm_key` for the resolution and active-key gate. @@ -419,7 +419,7 @@ async def _extract_user_id_from_request(request: Request) -> Optional[str]: return _active_key_user_id(resolved[1]) if resolved else None -async def _extract_active_key_hash_from_request(request: Request) -> Optional[str]: +async def _extract_active_key_hash_from_request(request: Request) -> str | None: """The hash of the litellm key that authorized the token request, when it maps to an active key. A DCR-bridge envelope seals this hash so admission can reload the live ``UserAPIKeyAuth`` record From 2f349f6cd18194a67b7c5985e989de5009bec4c9 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 16:16:52 -0700 Subject: [PATCH 296/399] fix(mcp): coerce numeric expires_in and make the active-key check total Two correctness gaps in the bridge mint. _bridge_grant_from_token_response only accepted an int expires_in, dropping a float (3600.0) or numeric-string ('3600') lifetime to None so the envelope fell back to its 1h cap and could outlive a shorter-lived upstream token; coerce it to a positive int (bool excluded). And _key_is_active called datetime.fromisoformat on the str|datetime expires outside the resolver's try, so a malformed stored expiry raised an unhandled 500 instead of the fail-closed invalid_request; it now fails closed (inactive) on an unparseable expiry. Regression tests cover int/float/string/bool coercion, the short-float TTL, and the malformed-expiry fail-closed path. --- .../mcp_server/discoverable_endpoints.py | 39 ++++++++++-- .../mcp_server/test_discoverable_endpoints.py | 60 +++++++++++++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index c5d7c3b40fd..00586a6afb3 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -342,12 +342,23 @@ def _key_is_active(key_obj: "UserAPIKeyAuth") -> bool: team-scoped or service-account key has no ``user_id`` yet is a legitimate credential, so gating on ``user_id`` presence would wrongly reject it. Callers that need the user (the per-user token store) derive it separately via :func:`_active_key_user_id`. + + Total by design: ``expires`` is typed ``str | datetime``, and an unparseable string would make + ``datetime.fromisoformat`` raise. Since the callers run this outside their key-resolution + ``try``, an uncaught parse error would surface as a 500 instead of the endpoint's fail-closed + behavior, so a malformed expiry is treated as inactive (return ``False``) rather than raising. """ if key_obj.blocked is True: return False expires = key_obj.expires if expires is not None: - expiry = expires if isinstance(expires, datetime) else datetime.fromisoformat(expires) + if isinstance(expires, datetime): + expiry = expires + else: + try: + expiry = datetime.fromisoformat(expires) + except (ValueError, TypeError): + return False if expiry.tzinfo is None or expiry.tzinfo.utcoffset(expiry) is None: expiry = expiry.replace(tzinfo=timezone.utc) if expiry < datetime.now(timezone.utc): @@ -698,10 +709,31 @@ async def authorize_with_server( return response +def _coerce_positive_expires_in(value: object) -> int | None: + """Coerce an upstream ``expires_in`` to a positive int, or ``None`` when it is absent or not a + usable number. IdPs return it as an int, a float (``3600.0``), or a numeric string (``"3600"``); + accepting only ``int`` would drop the float/string cases to ``None`` and fall back to the + envelope's 1h cap, which can outlive a shorter-lived upstream token and forward a stale bearer. + ``bool`` is excluded (it is an ``int`` subclass but never a real lifetime).""" + if isinstance(value, bool): + return None + if isinstance(value, (int, float)): + seconds = int(value) + return seconds if seconds > 0 else None + if isinstance(value, str): + try: + seconds = int(float(value.strip())) + except (ValueError, TypeError): + return None + return seconds if seconds > 0 else None + return None + + def _bridge_grant_from_token_response(token_response: object) -> Optional["UpstreamTokenGrant"]: """Validate an upstream OAuth token response into a typed grant, or None when it lacks a usable access token. Each field is isinstance-checked so nothing untyped from ``response.json()`` flows - into the grant.""" + into the grant; ``expires_in`` is numerically coerced so a float/string lifetime is honored + rather than dropped to the envelope's default cap.""" from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import UpstreamTokenGrant, ) @@ -714,13 +746,12 @@ def _bridge_grant_from_token_response(token_response: object) -> Optional["Upstr token_type = token_response.get("token_type") refresh = token_response.get("refresh_token") scope = token_response.get("scope") - expires_in = token_response.get("expires_in") return UpstreamTokenGrant( access_token=SecretStr(access), token_type=token_type if isinstance(token_type, str) and token_type else "Bearer", refresh_token=SecretStr(refresh) if isinstance(refresh, str) and refresh else None, scope=scope if isinstance(scope, str) and scope else None, - expires_in=expires_in if isinstance(expires_in, int) and expires_in > 0 else None, + expires_in=_coerce_positive_expires_in(token_response.get("expires_in")), ) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 2bf5e49ec79..0dc8d8116df 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4449,6 +4449,43 @@ async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm assert exc.value.detail["error"] == "invalid_request" +def test_bridge_grant_coerces_numeric_expires_in(): + """expires_in from an IdP may be an int, a float (3600.0), or a numeric string ("3600"); coerce + it to a positive int so the envelope TTL honors the real lifetime instead of dropping a non-int + value and defaulting to the 1h cap (which can outlive a shorter-lived upstream token). bool and + non-numeric values become None.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _bridge_grant_from_token_response, + ) + + def ei(v): + return _bridge_grant_from_token_response({"access_token": "x", "expires_in": v}).expires_in + + assert ei(300) == 300 + assert ei(300.0) == 300 + assert ei("300") == 300 + assert ei(" 300 ") == 300 + assert ei(True) is None + assert ei("nope") is None + assert ei(0) is None + assert ei(-5) is None + assert ei(None) is None + + +@pytest.mark.asyncio +async def test_bridge_token_exchange_honors_short_float_expires_in_ttl(): + """A short float expires_in from the upstream caps the envelope TTL, so the client-held envelope + does not outlive the upstream token. Before coercion a float was dropped and the envelope + defaulted to the 1h cap (3600), which would forward a stale bearer after the upstream token + expired.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UP", "token_type": "Bearer", "expires_in": 120.0} + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + assert json.loads(response.body)["expires_in"] <= 120 + + @pytest.mark.asyncio async def test_oauth_delegate_bridge_token_exchange_missing_access_token_is_502_not_keyerror(): """When the upstream token response has no access_token, a dcr_bridge oauth_delegate exchange @@ -4915,6 +4952,29 @@ async def test_extract_active_key_hash_rejects_blocked_key(proxy_globals): assert await _extract_active_key_hash_from_request(request) is None +@pytest.mark.asyncio +async def test_extract_active_key_hash_fails_closed_on_malformed_expiry(proxy_globals): + """A key whose stored expires string does not parse must fail closed to no-hash (the mint then + returns invalid_request), not surface an unhandled 500. The active-state check runs outside the + resolver's try, so it must be total over a bad expires rather than letting datetime.fromisoformat + raise. Before the fix this raised a ValueError instead of returning None.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _extract_active_key_hash_from_request, + ) + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + class _FakePrisma: + async def get_data(self, token, table_name, parent_otel_span=None, proxy_logging_obj=None): + return UserAPIKeyAuth(token=token, user_id="u", expires="not-a-parseable-date") + + proxy_globals.user_api_key_cache = UserApiKeyCache() + proxy_globals.prisma_client = _FakePrisma() + + request = _token_request({"x-litellm-api-key": "sk-bad-expiry-key"}) + assert await _extract_active_key_hash_from_request(request) is None + + @pytest.mark.asyncio async def test_extract_active_key_hash_none_without_litellm_key(proxy_globals): """No LiteLLM key on the request yields no hash without consulting the resolver.""" From 7a63e516252a5fc78a6150da4b3fc6cd03bab1c6 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 16:36:08 -0700 Subject: [PATCH 297/399] fix(mcp): harden the bridge token mint (multi-lens review pass) Findings from a full adversarial review of the mint path across security, correctness, error-handling, concurrency, and OAuth-protocol dimensions. - expires_in coercion is now total: int(float(...)) can raise OverflowError on Infinity / a giant numeric string, which escaped the ValueError/TypeError catch and 500'd the token endpoint. Unified to catch OverflowError too. - Resolve the litellm identity BEFORE exchanging the single-use upstream code, so a missing or transiently-unresolvable identity fails closed with invalid_request without burning the code (the mint re-resolves via a cache hit). - The no-identity failure is now an RFC 6749 5.2-shaped invalid_request (JSONResponse, top-level error, no-store) instead of a detail-wrapped HTTPException, matching the BYOK OAuth endpoint. - EnvelopeTooLarge (upstream token too big to seal) surfaces a 502, not a 500. - The upstream refresh_token is no longer sealed into the envelope: the edge never consumes it, so it was dead weight embedding a long-lived upstream credential in the client bearer and enlarging the envelope; refresh is a follow-up (a dedicated refresh-envelope). Security review found no exploitable defect (forgery, cross-server/user replay, leakage, confused-deputy all closed). Regression tests cover the OverflowError, the code-not-burned path, the RFC-shaped error, the 502, and the dropped refresh. --- .../mcp_server/discoverable_endpoints.py | 76 ++++++++++++------- .../mcp_server/test_discoverable_endpoints.py | 71 +++++++++++++++-- 2 files changed, 116 insertions(+), 31 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 00586a6afb3..4f72c2cb8c2 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -373,7 +373,7 @@ def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> str | None: return key_obj.user_id if _key_is_active(key_obj) else None -async def _resolve_active_litellm_key(request: Request) -> Tuple[str, "UserAPIKeyAuth"] | None: +async def _resolve_active_litellm_key(request: Request) -> tuple[str, "UserAPIKeyAuth"] | None: """Resolve the presented litellm key to ``(its hash, the live active key record)``, or ``None`` when the key is absent, unresolvable, or blocked/expired. @@ -714,19 +714,17 @@ def _coerce_positive_expires_in(value: object) -> int | None: usable number. IdPs return it as an int, a float (``3600.0``), or a numeric string (``"3600"``); accepting only ``int`` would drop the float/string cases to ``None`` and fall back to the envelope's 1h cap, which can outlive a shorter-lived upstream token and forward a stale bearer. - ``bool`` is excluded (it is an ``int`` subclass but never a real lifetime).""" - if isinstance(value, bool): + ``bool`` is excluded (it is an ``int`` subclass but never a real lifetime). Total over hostile + input: a non-numeric string, ``NaN``, ``Infinity``, or an over-large value all resolve to + ``None`` rather than raising (``int(float(...))`` can raise ``ValueError`` or ``OverflowError``), + so a malformed upstream ``expires_in`` never surfaces as a 500 from the token endpoint.""" + if isinstance(value, bool) or not isinstance(value, (int, float, str)): return None - if isinstance(value, (int, float)): - seconds = int(value) - return seconds if seconds > 0 else None - if isinstance(value, str): - try: - seconds = int(float(value.strip())) - except (ValueError, TypeError): - return None - return seconds if seconds > 0 else None - return None + try: + seconds = int(float(value)) + except (ValueError, TypeError, OverflowError): + return None + return seconds if seconds > 0 else None def _bridge_grant_from_token_response(token_response: object) -> Optional["UpstreamTokenGrant"]: @@ -744,17 +742,38 @@ def _bridge_grant_from_token_response(token_response: object) -> Optional["Upstr if not isinstance(access, str) or not access: return None token_type = token_response.get("token_type") - refresh = token_response.get("refresh_token") scope = token_response.get("scope") return UpstreamTokenGrant( access_token=SecretStr(access), token_type=token_type if isinstance(token_type, str) and token_type else "Bearer", - refresh_token=SecretStr(refresh) if isinstance(refresh, str) and refresh else None, + # The upstream refresh_token is deliberately NOT sealed: the edge never consumes it (it forwards + # only token_type + access_token), so it would be dead weight embedding a long-lived upstream + # credential in the client-held bearer, and it enlarges the envelope. Refresh support is a + # follow-up (a dedicated refresh-envelope); the client re-runs authorization_code at the cap. + refresh_token=None, scope=scope if isinstance(scope, str) and scope else None, expires_in=_coerce_positive_expires_in(token_response.get("expires_in")), ) +def _bridge_invalid_request_response() -> JSONResponse: + """RFC 6749 §5.2-shaped ``invalid_request`` for a bridge token exchange that carries no resolvable + litellm identity. Returned (not raised) so the OAuth error members sit at the top level rather than + wrapped in FastAPI's ``detail``, with the no-store token-endpoint headers, matching the BYOK OAuth + endpoint and what a strict DCR client parses per RFC 6749 §5.2.""" + return JSONResponse( + status_code=400, + content={ + "error": "invalid_request", + "error_description": ( + "this server issues a gateway-bound credential; send a litellm credential " + "(x-litellm-api-key or Authorization) on the token request" + ), + }, + headers=TOKEN_NO_CACHE_HEADERS, + ) + + async def _mint_bridge_delegate_token_response( request: Request, mcp_server: MCPServer, token_response: object ) -> JSONResponse: @@ -784,16 +803,7 @@ async def _mint_bridge_delegate_token_response( key_hash = await _extract_active_key_hash_from_request(request) if not key_hash: - raise HTTPException( - status_code=400, - detail={ - "error": "invalid_request", - "error_description": ( - "this server issues a gateway-bound credential; send a litellm credential " - "(x-litellm-api-key or Authorization) on the token request" - ), - }, - ) + return _bridge_invalid_request_response() grant = _bridge_grant_from_token_response(token_response) if grant is None: @@ -804,7 +814,11 @@ async def _mint_bridge_delegate_token_response( identity = EnvelopeIdentity(server_id=mcp_server.server_id, key_hash=key_hash) sealed = build_bridge_token_response(identity, grant, keys, now) if not isinstance(sealed, SealedEnvelope): - raise HTTPException(status_code=500, detail="Failed to mint the gateway-bound credential") + # build_bridge_token_response returns EnvelopeTooLarge as a value when the upstream token is + # too large to seal; that is an upstream-payload condition, so surface a 502, not a 500. + raise HTTPException( + status_code=502, detail="Upstream token is too large to seal into a gateway-bound credential" + ) expires_in = max(1, int((sealed.expires_at - now).total_seconds())) body = {"access_token": sealed.token.get_secret_value(), "token_type": "Bearer", "expires_in": expires_in} @@ -884,6 +898,16 @@ async def exchange_token_with_server( if code_verifier: token_data["code_verifier"] = code_verifier + # For a bridge oauth_delegate mint, resolve the litellm identity BEFORE exchanging the + # single-use upstream code. A missing or transiently-unresolvable identity then fails closed + # with invalid_request without consuming the code, so the client can retry the same code + # instead of being forced back through the full interactive authorize. The mint below + # re-resolves authoritatively; get_key_object is cache-first, so that second call is a cache + # hit and this adds no extra database round-trip. + if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: + if not await _extract_active_key_hash_from_request(request): + return _bridge_invalid_request_response() + async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.Oauth2Check) response = await async_client.post( mcp_server.token_url, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 0dc8d8116df..8aad8b24e88 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4365,7 +4365,7 @@ async def test_register_bridge_relay_never_persists(): _BRIDGE_MASTER_KEY = "sk-bridge-producer-master-key-0123456789abcdef" -async def _exchange_for_bridge_server(server, upstream_body, key_hash): +async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_client_out=None): from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( exchange_token_with_server, ) @@ -4375,6 +4375,8 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash): fake_http_response.raise_for_status = MagicMock() fake_http_client = MagicMock() fake_http_client.post = AsyncMock(return_value=fake_http_response) + if fake_client_out is not None: + fake_client_out["client"] = fake_http_client with ( patch( @@ -4436,17 +4438,69 @@ async def test_oauth_delegate_bridge_token_exchange_mints_envelope_not_raw_token @pytest.mark.asyncio async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm_identity(): """Without a resolvable litellm identity on the token request, the exchange must not mint an - identity-less envelope; it returns an OAuth invalid_request so the client sends a credential.""" + identity-less envelope. It returns an RFC 6749 §5.2-shaped invalid_request (error at the top + level, not wrapped in detail) BEFORE exchanging the upstream code, so the single-use code is not + burned and the client can retry.""" from litellm.types.mcp import MCPAuth server = _bridge_server(auth_type=MCPAuth.oauth_delegate) upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + captured: dict = {} + response = await _exchange_for_bridge_server(server, upstream, key_hash=None, fake_client_out=captured) + assert response.status_code == 400 + assert json.loads(response.body)["error"] == "invalid_request" + # identity resolution failed first, so the upstream single-use code was never exchanged (not burned) + captured["client"].post.assert_not_called() + + +@pytest.mark.asyncio +async def test_bridge_envelope_too_large_upstream_token_is_502(): + """An upstream token too large to seal into the envelope is an upstream-payload condition, so the + mint surfaces a 502 rather than a 500 (build_bridge_token_response returns EnvelopeTooLarge as a + value, and the caller maps it to a truthful status).""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "x" * 40000, "token_type": "Bearer", "expires_in": 3600} with pytest.raises(HTTPException) as exc: - await _exchange_for_bridge_server(server, upstream, key_hash=None) + await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + assert exc.value.status_code == 502 - assert exc.value.status_code == 400 - assert exc.value.detail["error"] == "invalid_request" + +@pytest.mark.asyncio +async def test_bridge_envelope_does_not_seal_upstream_refresh_token(): + """The upstream refresh_token is never sealed into the client-held envelope: the edge never + consumes it and a long-lived upstream credential should not live in the client bearer. The opened + envelope's grant carries no refresh token even when the upstream returned one, and neither does + the response body.""" + from datetime import datetime, timezone + + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + envelope_keys_from_master_key, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + OpenedEnvelope, + open_envelope, + ) + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = { + "access_token": "UP", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "UPSTREAM-REFRESH", + } + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + + body = json.loads(response.body) + assert "refresh_token" not in body + assert "UPSTREAM-REFRESH" not in body["access_token"] + keys = envelope_keys_from_master_key(_BRIDGE_MASTER_KEY) + opened = open_envelope(body["access_token"], keys, datetime.now(timezone.utc)) + assert isinstance(opened, OpenedEnvelope) + assert opened.grant.refresh_token is None def test_bridge_grant_coerces_numeric_expires_in(): @@ -4470,6 +4524,13 @@ def test_bridge_grant_coerces_numeric_expires_in(): assert ei(0) is None assert ei(-5) is None assert ei(None) is None + # hostile numerics must not raise (int(float(...)) can OverflowError) -> None + assert ei("inf") is None + assert ei("1e999") is None + assert ei("-inf") is None + assert ei("nan") is None + assert ei(float("inf")) is None + assert ei(10**400) is None @pytest.mark.asyncio From e16ad044c3773ac958b1cffff0ad6d15bb5e0296 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 16:58:14 -0700 Subject: [PATCH 298/399] fix(mcp): close the burn-before-check gate for both grants and validate master_key first Follow-up to the pre-exchange identity gate, which I had only added to the authorization_code branch and which left the master_key check inside the mint (after the upstream exchange) - so the very burn-then-fail pattern it was meant to prevent still applied to refresh_token grants and to a misconfigured gateway. - Hoist a single pre-exchange gate above the upstream call that covers BOTH grant types: it fails closed (invalid_request) on an unresolvable litellm identity and 500s on an unset master_key BEFORE the single-use code or refresh token is exchanged/rotated, so a bad key or a misconfigured gateway never burns the upstream credential. - Report expires_in from the envelope JWT's own second-truncated exp (rounding the elapsed portion up) instead of the raw expires_at - now delta, so the client is never told the bearer is valid past the ~1s point admission already expires it. Regression tests assert the upstream exchange is never called on the no-identity refresh grant and the master_key-unset path, and that the reported expires_in does not overstate the JWT exp. --- .../mcp_server/discoverable_endpoints.py | 28 ++++-- .../mcp_server/test_discoverable_endpoints.py | 97 +++++++++++++++++++ 2 files changed, 115 insertions(+), 10 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 4f72c2cb8c2..5bf4de09bba 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -1,6 +1,7 @@ import asyncio import html as _html import json +import math import secrets import time from datetime import datetime, timezone @@ -820,7 +821,10 @@ async def _mint_bridge_delegate_token_response( status_code=502, detail="Upstream token is too large to seal into a gateway-bound credential" ) - expires_in = max(1, int((sealed.expires_at - now).total_seconds())) + # The JWT exp is int(expires_at.timestamp()) (second-truncated), and admission expires the envelope + # against that exp. Report expires_in from the same truncated exp, rounding the elapsed portion up, + # so the client is never told the bearer lives past the point admission already rejects it. + expires_in = max(1, int(sealed.expires_at.timestamp()) - math.ceil(now.timestamp())) body = {"access_token": sealed.token.get_secret_value(), "token_type": "Bearer", "expires_in": expires_in} return JSONResponse(body, headers=TOKEN_NO_CACHE_HEADERS) @@ -898,15 +902,19 @@ async def exchange_token_with_server( if code_verifier: token_data["code_verifier"] = code_verifier - # For a bridge oauth_delegate mint, resolve the litellm identity BEFORE exchanging the - # single-use upstream code. A missing or transiently-unresolvable identity then fails closed - # with invalid_request without consuming the code, so the client can retry the same code - # instead of being forced back through the full interactive authorize. The mint below - # re-resolves authoritatively; get_key_object is cache-first, so that second call is a cache - # hit and this adds no extra database round-trip. - if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: - if not await _extract_active_key_hash_from_request(request): - return _bridge_invalid_request_response() + # A bridge oauth_delegate mint must fail closed BEFORE the upstream exchange consumes or rotates the + # single-use code (or refresh token): confirm the gateway can mint at all (master_key set) and that + # the request carries a resolvable litellm identity. Applies to both grant types, so an invalid key + # or a misconfigured gateway never burns the upstream credential. The mint below re-checks + # authoritatively; get_key_object is cache-first, so the identity re-resolution is a cache hit and + # adds no extra database round-trip. + if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: + from litellm.proxy.proxy_server import master_key as _bridge_master_key # noqa: PLC0415 + + if not _bridge_master_key: + raise HTTPException(status_code=500, detail="Server misconfigured: master_key is not set") + if not await _extract_active_key_hash_from_request(request): + return _bridge_invalid_request_response() async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.Oauth2Check) response = await async_client.post( diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 8aad8b24e88..4a4ff398915 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4503,6 +4503,103 @@ async def test_bridge_envelope_does_not_seal_upstream_refresh_token(): assert opened.grant.refresh_token is None +@pytest.mark.asyncio +async def test_bridge_refresh_grant_fails_closed_before_upstream_when_no_identity(): + """The pre-exchange identity gate covers the refresh_token grant, not just authorization_code: an + unresolvable litellm identity fails closed with invalid_request BEFORE the upstream refresh is + exchanged, so the client's refresh token is not rotated/consumed on a rejected request.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import exchange_token_with_server + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock() + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", + new=AsyncMock(return_value=None), + ), + patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), + ): + response = await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=server, + grant_type="refresh_token", + code=None, + redirect_uri=None, + client_id="dcr-client-123", + client_secret=None, + code_verifier=None, + refresh_token="client-refresh-token", + ) + + assert response.status_code == 400 + assert json.loads(response.body)["error"] == "invalid_request" + fake_http_client.post.assert_not_called() + + +@pytest.mark.asyncio +async def test_bridge_mint_fails_closed_before_upstream_when_master_key_unset(): + """master_key is validated BEFORE the upstream exchange, so a misconfigured gateway 500s without + consuming the single-use code, avoiding the burn-then-fail the pre-exchange gate exists to prevent.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import exchange_token_with_server + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock() + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", + new=AsyncMock(return_value="hashed-litellm-key-77"), + ), + patch("litellm.proxy.proxy_server.master_key", None), + ): + with pytest.raises(HTTPException) as exc: + await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri="https://claude.ai/api/mcp/auth_callback", + client_id="dcr-client-123", + client_secret=None, + code_verifier="verifier", + ) + + assert exc.value.status_code == 500 + fake_http_client.post.assert_not_called() + + +@pytest.mark.asyncio +async def test_bridge_reported_expires_in_does_not_overstate_jwt_exp(): + """The reported expires_in is derived from the envelope JWT's second-truncated exp (rounding the + elapsed portion up), so the client is never told the bearer lives past the point admission expires + it. Regression for the sub-second overstatement of the raw (expires_at - now) delta.""" + import time + + import jwt as _jwt + + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UP", "token_type": "Bearer", "expires_in": 300} + before = int(time.time()) + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + body = json.loads(response.body) + claims = _jwt.decode(body["access_token"].removeprefix("llm_env_"), options={"verify_signature": False}) + # projecting the reported lifetime from a time no later than the mint must not exceed the JWT exp + assert before + body["expires_in"] <= claims["exp"] + + def test_bridge_grant_coerces_numeric_expires_in(): """expires_in from an IdP may be an int, a float (3600.0), or a numeric string ("3600"); coerce it to a positive int so the envelope TTL honors the real lifetime instead of dropping a non-int From 2f0ddc82f7f38b282dff5e299436cd99285e1ba7 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 17:22:01 -0700 Subject: [PATCH 299/399] refactor(mcp): make the bridge delegate mint a phased failures-as-values pipeline The dcr_bridge oauth_delegate token mint validated its preconditions in two places: a pre-exchange guard inside exchange_token_with_server (master_key set, resolvable litellm identity) and an authoritative re-check inside the post-exchange _mint_bridge_delegate_token_response. Keeping the two in step by hand is what kept producing the same class of finding: a precondition guarded on one grant branch but not the other, master_key checked after the exchange on one path, identity resolved twice, and each failure raising an ad-hoc HTTPException with its own status and body shape. Model the mint as three phases whose failures are values. _prepare_bridge_mint runs before the exchange, checks every precondition once (master_key, then identity), and returns either a frozen _BridgeMintReady carrying the resolved key hash and the master-key-derived envelope keys, or a _BridgeMintError literal. Because every precondition lives in prepare, and prepare runs before the upstream POST, no failure can burn the single-use code or rotate a refresh token, for either grant type, by construction rather than by a guard we have to remember to keep in sync. _finish_bridge_mint runs after the exchange and has no preconditions left that can fail; its only failure values are properties of the upstream response itself (no usable access_token, or a token too large to seal). One mapper, _bridge_mint_error_response, turns each _BridgeMintError into an RFC 6749 section 5.2-shaped body with a status truthful about where the failure is (400 for the caller, 500 for gateway config, 502 for the upstream), with an exhaustive match plus assert_never so a new failure mode cannot be added without a matching status. Behavior is unchanged for the client. Every failure that previously raised now returns the same status as an OAuth error body, which is the correct token-endpoint contract; the three tests that asserted a raised HTTPException now assert the returned response. _exchange_for_bridge_server additionally asserts the identity resolver is awaited exactly once for a bridge server and never for a non-bridge one. --- .../mcp_server/discoverable_endpoints.py | 167 +++++++++++------- .../mcp_server/test_discoverable_endpoints.py | 63 ++++--- 2 files changed, 140 insertions(+), 90 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 5bf4de09bba..1bdbc8ea8a4 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -4,6 +4,7 @@ import json import math import secrets import time +from dataclasses import dataclass from datetime import datetime, timezone from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Tuple from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse @@ -12,6 +13,7 @@ import httpx from fastapi import APIRouter, Form, HTTPException, Request from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response from pydantic import BaseModel, SecretStr, ValidationError +from typing_extensions import assert_never from litellm._logging import verbose_logger from litellm.llms.custom_httpx.http_handler import ( @@ -39,6 +41,7 @@ from litellm.types.mcp_server.mcp_server_manager import MCPServer if TYPE_CHECKING: from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + EnvelopeKeys, UpstreamTokenGrant, ) from litellm.proxy._types import LiteLLM_MCPServerTable, UserAPIKeyAuth @@ -757,73 +760,112 @@ def _bridge_grant_from_token_response(token_response: object) -> Optional["Upstr ) -def _bridge_invalid_request_response() -> JSONResponse: - """RFC 6749 §5.2-shaped ``invalid_request`` for a bridge token exchange that carries no resolvable - litellm identity. Returned (not raised) so the OAuth error members sit at the top level rather than - wrapped in FastAPI's ``detail``, with the no-store token-endpoint headers, matching the BYOK OAuth - endpoint and what a strict DCR client parses per RFC 6749 §5.2.""" - return JSONResponse( - status_code=400, - content={ - "error": "invalid_request", - "error_description": ( +# --------------------------------------------------------------------------- +# DCR-bridge oauth_delegate mint: a three-phase pipeline whose failures are values. +# +# prepare (before the upstream exchange) -> validate every precondition and resolve identity+keys +# exchange (the single-use upstream code is consumed here, in exchange_token_with_server) +# finish (after the exchange) -> seal the upstream grant into the client-held envelope +# +# Every precondition lives in ``prepare``, which runs BEFORE the exchange, so no failure can burn the +# single-use code or rotate a refresh token, for either grant type -- that whole class of bug is gone +# by construction rather than guarded case by case. Failures are values mapped to an OAuth-shaped +# response in one place (``_bridge_mint_error_response``), so status codes and the RFC 6749 §5.2 body +# shape are uniform. Adding a failure mode is a new literal plus a match arm the type checker forces. +# --------------------------------------------------------------------------- + +_BridgeMintError = Literal["not_configured", "no_identity", "no_upstream_token", "too_large"] + + +@dataclass(frozen=True, slots=True) +class _BridgeMintReady: + """Everything the seal needs, resolved once before the exchange: the authorizing key hash and the + master-key-derived envelope keys. Passing this forward means identity resolution and key derivation + happen exactly once, and ``_finish_bridge_mint`` has no preconditions left that could fail.""" + + key_hash: str + keys: "EnvelopeKeys" + + +def _bridge_mint_error_response(error: _BridgeMintError) -> JSONResponse: + """Map a bridge-mint failure value to its token-endpoint response. One place, RFC 6749 §5.2 shape + (top-level ``error``, no-store) for every case, with a status truthful about where the failure is: + the caller's request (400), the gateway config (500), or the upstream (502).""" + if error == "no_identity": + status, code, desc = ( + 400, + "invalid_request", + ( "this server issues a gateway-bound credential; send a litellm credential " "(x-litellm-api-key or Authorization) on the token request" ), - }, - headers=TOKEN_NO_CACHE_HEADERS, + ) + elif error == "not_configured": + status, code, desc = ( + 500, + "server_error", + ("the gateway is not configured to mint a gateway-bound credential (master_key is not set)"), + ) + elif error == "no_upstream_token": + status, code, desc = 502, "server_error", "the upstream token response has no usable access_token" + elif error == "too_large": + status, code, desc = ( + 502, + "server_error", + ("the upstream token is too large to seal into a gateway-bound credential"), + ) + else: + assert_never(error) + return JSONResponse( + status_code=status, content={"error": code, "error_description": desc}, headers=TOKEN_NO_CACHE_HEADERS ) -async def _mint_bridge_delegate_token_response( - request: Request, mcp_server: MCPServer, token_response: object -) -> JSONResponse: - """Return the client-held envelope bearer for a DCR-bridge ``oauth_delegate`` token exchange. +async def _prepare_bridge_mint(request: Request, mcp_server: MCPServer) -> "_BridgeMintReady | _BridgeMintError": + """Phase 1, BEFORE the upstream exchange: validate that the gateway can mint (master_key set) and + that the request carries a resolvable litellm identity, and derive the envelope keys. Returns a + ready context or a failure value. Running before the exchange is what makes a missing master_key or + an unresolvable identity fail closed without consuming the single-use code / rotating a refresh + token, for both grant types.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import + envelope_keys_from_master_key, + ) + from litellm.proxy.proxy_server import ( # noqa: PLC0415 # inline import avoids a module-load circular import + master_key, + ) - The envelope binds the authorizing litellm key (its hash, resolved from the token request) to the - upstream grant, so the client holds one bearer that later admits it and forwards the upstream - token, with nothing stored server-side. Admission reloads the live key by that hash, so the key's - current restrictions and revocation gate the request. Fails closed with an OAuth - ``invalid_request`` when no active litellm key accompanies the token request rather than minting - an unbound credential. - """ + if not master_key: + return "not_configured" + key_hash = await _extract_active_key_hash_from_request(request) + if not key_hash: + return "no_identity" + return _BridgeMintReady(key_hash=key_hash, keys=envelope_keys_from_master_key(master_key)) + + +def _finish_bridge_mint( + ready: "_BridgeMintReady", mcp_server: MCPServer, token_response: object, now: datetime +) -> "JSONResponse | _BridgeMintError": + """Phase 3, AFTER the upstream exchange: seal the upstream grant into the client-held envelope using + the pre-resolved identity and keys, so the client holds one bearer that admits it and forwards the + upstream token with nothing stored server-side. The only failures here are properties of the + upstream response (no usable token, or a token too large to seal), returned as values.""" from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import build_bridge_token_response, - envelope_keys_from_master_key, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import EnvelopeIdentity, SealedEnvelope, ) - from litellm.proxy.proxy_server import ( - master_key, # noqa: PLC0415 # inline import avoids a module-load circular import - ) - - if not master_key: - raise HTTPException(status_code=500, detail="Server misconfigured: master_key is not set") - - key_hash = await _extract_active_key_hash_from_request(request) - if not key_hash: - return _bridge_invalid_request_response() grant = _bridge_grant_from_token_response(token_response) if grant is None: - raise HTTPException(status_code=502, detail="Upstream token response has no usable access_token") - - now = datetime.now(timezone.utc) - keys = envelope_keys_from_master_key(master_key) - identity = EnvelopeIdentity(server_id=mcp_server.server_id, key_hash=key_hash) - sealed = build_bridge_token_response(identity, grant, keys, now) + return "no_upstream_token" + identity = EnvelopeIdentity(server_id=mcp_server.server_id, key_hash=ready.key_hash) + sealed = build_bridge_token_response(identity, grant, ready.keys, now) if not isinstance(sealed, SealedEnvelope): - # build_bridge_token_response returns EnvelopeTooLarge as a value when the upstream token is - # too large to seal; that is an upstream-payload condition, so surface a 502, not a 500. - raise HTTPException( - status_code=502, detail="Upstream token is too large to seal into a gateway-bound credential" - ) - - # The JWT exp is int(expires_at.timestamp()) (second-truncated), and admission expires the envelope - # against that exp. Report expires_in from the same truncated exp, rounding the elapsed portion up, - # so the client is never told the bearer lives past the point admission already rejects it. + return "too_large" + # Report expires_in from the JWT's own second-truncated exp, rounding the elapsed portion up, so the + # client is never told the bearer lives past the point admission (which uses that exp) rejects it. expires_in = max(1, int(sealed.expires_at.timestamp()) - math.ceil(now.timestamp())) body = {"access_token": sealed.token.get_secret_value(), "token_type": "Bearer", "expires_in": expires_in} return JSONResponse(body, headers=TOKEN_NO_CACHE_HEADERS) @@ -902,19 +944,15 @@ async def exchange_token_with_server( if code_verifier: token_data["code_verifier"] = code_verifier - # A bridge oauth_delegate mint must fail closed BEFORE the upstream exchange consumes or rotates the - # single-use code (or refresh token): confirm the gateway can mint at all (master_key set) and that - # the request carries a resolvable litellm identity. Applies to both grant types, so an invalid key - # or a misconfigured gateway never burns the upstream credential. The mint below re-checks - # authoritatively; get_key_object is cache-first, so the identity re-resolution is a cache hit and - # adds no extra database round-trip. + # Phase 1: for a bridge oauth_delegate mint, validate all preconditions and resolve identity+keys + # BEFORE the exchange below consumes the single-use upstream code, and carry the ready context to + # phase 3. A failure here returns without ever touching the upstream credential. + bridge_mint_ready: _BridgeMintReady | None = None if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: - from litellm.proxy.proxy_server import master_key as _bridge_master_key # noqa: PLC0415 - - if not _bridge_master_key: - raise HTTPException(status_code=500, detail="Server misconfigured: master_key is not set") - if not await _extract_active_key_hash_from_request(request): - return _bridge_invalid_request_response() + prepared = await _prepare_bridge_mint(request, mcp_server) + if not isinstance(prepared, _BridgeMintReady): + return _bridge_mint_error_response(prepared) + bridge_mint_ready = prepared async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.Oauth2Check) response = await async_client.post( @@ -982,8 +1020,11 @@ async def exchange_token_with_server( # A DCR-bridge oauth_delegate server hands the client a gateway-bound envelope (identity plus the # upstream token) instead of the raw upstream token, so the one bearer both admits the caller and # forwards the upstream credential. Only this mode mints; every other server returns the raw token. - if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: - return await _mint_bridge_delegate_token_response(request, mcp_server, token_response) + if bridge_mint_ready is not None: + # Phase 3: seal the upstream grant into the client-held envelope; failures map through the same + # OAuth-shaped response as the phase-1 preconditions. + minted = _finish_bridge_mint(bridge_mint_ready, mcp_server, token_response, datetime.now(timezone.utc)) + return minted if isinstance(minted, JSONResponse) else _bridge_mint_error_response(minted) result = { "access_token": token_response["access_token"], diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 4a4ff398915..4bbaf7b0b76 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4375,6 +4375,7 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_clie fake_http_response.raise_for_status = MagicMock() fake_http_client = MagicMock() fake_http_client.post = AsyncMock(return_value=fake_http_response) + key_resolver = AsyncMock(return_value=key_hash) if fake_client_out is not None: fake_client_out["client"] = fake_http_client @@ -4385,11 +4386,11 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_clie ), patch( "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", - new=AsyncMock(return_value=key_hash), + new=key_resolver, ), patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), ): - return await exchange_token_with_server( + response = await exchange_token_with_server( request=_bridge_mock_request(), mcp_server=server, grant_type="authorization_code", @@ -4399,6 +4400,11 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_clie client_secret=None, code_verifier="verifier", ) + if server.is_oauth_delegate and server.is_dcr_bridge: + key_resolver.assert_awaited_once() + else: + key_resolver.assert_not_awaited() + return response @pytest.mark.asyncio @@ -4457,15 +4463,16 @@ async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm @pytest.mark.asyncio async def test_bridge_envelope_too_large_upstream_token_is_502(): """An upstream token too large to seal into the envelope is an upstream-payload condition, so the - mint surfaces a 502 rather than a 500 (build_bridge_token_response returns EnvelopeTooLarge as a - value, and the caller maps it to a truthful status).""" + mint surfaces a 502 (as an RFC 6749 §5.2 error body, not a raised HTTPException) rather than a 500: + build_bridge_token_response returns EnvelopeTooLarge as a value, _finish_bridge_mint returns the + "too_large" failure, and _bridge_mint_error_response maps it to a truthful status.""" from litellm.types.mcp import MCPAuth server = _bridge_server(auth_type=MCPAuth.oauth_delegate) upstream = {"access_token": "x" * 40000, "token_type": "Bearer", "expires_in": 3600} - with pytest.raises(HTTPException) as exc: - await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") - assert exc.value.status_code == 502 + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + assert response.status_code == 502 + assert json.loads(response.body)["error"] == "server_error" @pytest.mark.asyncio @@ -4544,8 +4551,10 @@ async def test_bridge_refresh_grant_fails_closed_before_upstream_when_no_identit @pytest.mark.asyncio async def test_bridge_mint_fails_closed_before_upstream_when_master_key_unset(): - """master_key is validated BEFORE the upstream exchange, so a misconfigured gateway 500s without - consuming the single-use code, avoiding the burn-then-fail the pre-exchange gate exists to prevent.""" + """master_key is validated BEFORE the upstream exchange (in _prepare_bridge_mint), so a + misconfigured gateway returns a 500 server_error without consuming the single-use code, avoiding + the burn-then-fail the pre-exchange phase exists to prevent. The failure is returned as an RFC 6749 + error body, not raised.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import exchange_token_with_server from litellm.types.mcp import MCPAuth @@ -4563,19 +4572,19 @@ async def test_bridge_mint_fails_closed_before_upstream_when_master_key_unset(): ), patch("litellm.proxy.proxy_server.master_key", None), ): - with pytest.raises(HTTPException) as exc: - await exchange_token_with_server( - request=_bridge_mock_request(), - mcp_server=server, - grant_type="authorization_code", - code="auth-code", - redirect_uri="https://claude.ai/api/mcp/auth_callback", - client_id="dcr-client-123", - client_secret=None, - code_verifier="verifier", - ) + response = await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri="https://claude.ai/api/mcp/auth_callback", + client_id="dcr-client-123", + client_secret=None, + code_verifier="verifier", + ) - assert exc.value.status_code == 500 + assert response.status_code == 500 + assert json.loads(response.body)["error"] == "server_error" fake_http_client.post.assert_not_called() @@ -4647,18 +4656,18 @@ async def test_bridge_token_exchange_honors_short_float_expires_in_ttl(): @pytest.mark.asyncio async def test_oauth_delegate_bridge_token_exchange_missing_access_token_is_502_not_keyerror(): """When the upstream token response has no access_token, a dcr_bridge oauth_delegate exchange - returns a clean 502 rather than raising a KeyError. The eager access_token extraction used to run - before the bridge branch, so a missing token raised KeyError and _bridge_grant_from_token_response's - nil guard (which maps to 502) was dead code; the extraction now lives on the non-bridge path only.""" + returns a clean 502 error body rather than raising a KeyError. _finish_bridge_mint asks + _bridge_grant_from_token_response for a typed grant, gets None, and returns the "no_upstream_token" + failure, which maps to 502; nothing indexes token_response["access_token"] on the bridge path.""" from litellm.types.mcp import MCPAuth server = _bridge_server(auth_type=MCPAuth.oauth_delegate) upstream = {"token_type": "Bearer", "expires_in": 3600} - with pytest.raises(HTTPException) as exc: - await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") - assert exc.value.status_code == 502 + assert response.status_code == 502 + assert json.loads(response.body)["error"] == "server_error" @pytest.mark.asyncio From 4ba7221b7a1ae717d55ff548247aa8416dbe8232 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 17:47:54 -0700 Subject: [PATCH 300/399] fix(mcp): let the bridge envelope report expires_in 0 at the jwt exp boundary _finish_bridge_mint floored the reported expires_in at 1. Admission expires the envelope against the JWT's second-truncated exp, so when the mint lands in the same second that exp falls on (a sub-second upstream lifetime, for instance), the true remaining life is 0 and reporting 1 tells the client the bearer lives one second past the point admission already rejects it. Floor at 0 instead so the reported lifetime never overstates the exp; the value still cannot go negative. The regression pins the boundary directly: minting at now=100.25 with a 1s upstream token seals exp=101, and the reported expires_in is max(0, 101 - ceil(100.25)) = 0. Under the old floor of 1 it reads 1, so the test fails on that mutation. Also drops the unused mcp_server parameter from _prepare_bridge_mint; identity and key derivation there never referenced the server. --- .../mcp_server/discoverable_endpoints.py | 6 ++-- .../mcp_server/test_discoverable_endpoints.py | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 1bdbc8ea8a4..abb375b5b6a 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -821,7 +821,7 @@ def _bridge_mint_error_response(error: _BridgeMintError) -> JSONResponse: ) -async def _prepare_bridge_mint(request: Request, mcp_server: MCPServer) -> "_BridgeMintReady | _BridgeMintError": +async def _prepare_bridge_mint(request: Request) -> "_BridgeMintReady | _BridgeMintError": """Phase 1, BEFORE the upstream exchange: validate that the gateway can mint (master_key set) and that the request carries a resolvable litellm identity, and derive the envelope keys. Returns a ready context or a failure value. Running before the exchange is what makes a missing master_key or @@ -866,7 +866,7 @@ def _finish_bridge_mint( return "too_large" # Report expires_in from the JWT's own second-truncated exp, rounding the elapsed portion up, so the # client is never told the bearer lives past the point admission (which uses that exp) rejects it. - expires_in = max(1, int(sealed.expires_at.timestamp()) - math.ceil(now.timestamp())) + expires_in = max(0, int(sealed.expires_at.timestamp()) - math.ceil(now.timestamp())) body = {"access_token": sealed.token.get_secret_value(), "token_type": "Bearer", "expires_in": expires_in} return JSONResponse(body, headers=TOKEN_NO_CACHE_HEADERS) @@ -949,7 +949,7 @@ async def exchange_token_with_server( # phase 3. A failure here returns without ever touching the upstream credential. bridge_mint_ready: _BridgeMintReady | None = None if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: - prepared = await _prepare_bridge_mint(request, mcp_server) + prepared = await _prepare_bridge_mint(request) if not isinstance(prepared, _BridgeMintReady): return _bridge_mint_error_response(prepared) bridge_mint_ready = prepared diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 4bbaf7b0b76..4aa23247249 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4609,6 +4609,35 @@ async def test_bridge_reported_expires_in_does_not_overstate_jwt_exp(): assert before + body["expires_in"] <= claims["exp"] +def test_bridge_reported_expires_in_can_be_zero_at_jwt_exp_boundary(): + from datetime import datetime, timezone + + from fastapi.responses import JSONResponse + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _BridgeMintReady, + _finish_bridge_mint, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + envelope_keys_from_master_key, + ) + from litellm.types.mcp import MCPAuth + + ready = _BridgeMintReady( + key_hash="hashed-litellm-key-77", + keys=envelope_keys_from_master_key(_BRIDGE_MASTER_KEY), + ) + response = _finish_bridge_mint( + ready=ready, + mcp_server=_bridge_server(auth_type=MCPAuth.oauth_delegate), + token_response={"access_token": "UP", "expires_in": 1}, + now=datetime.fromtimestamp(100.25, tz=timezone.utc), + ) + + assert isinstance(response, JSONResponse) + assert json.loads(response.body)["expires_in"] == 0 + + def test_bridge_grant_coerces_numeric_expires_in(): """expires_in from an IdP may be an int, a float (3600.0), or a numeric string ("3600"); coerce it to a positive int so the envelope TTL honors the real lifetime instead of dropping a non-int From a07aba05798941c75e369789831e701801355daa Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 18:28:32 -0700 Subject: [PATCH 301/399] refactor(mcp): make bridge-mint resolvers return tagged unions so status is truthful by construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three findings landed together, all one defect: a resolution step crushed several distinct outcomes into a single None or a silent default, so the mint's error mapper could not tell them apart and assigned the wrong status. Identity resolution mapped a database outage to the same None as a missing credential, which the mint reported as 400 invalid_request, blaming the caller for a gateway outage while admission statuses the same outage 503/500. Lifetime coercion mapped an explicit non-positive expires_in to the same None as an absent one, so an upstream token the IdP reports as already dead was sealed into an hour-long envelope. And the refresh_token grant was run through the upstream exchange (which can rotate the client's upstream refresh credential) and its result then discarded, even though a bridge server seals no refresh_token and the client never holds one to present. Rather than add a mapping branch per finding, the fix changes the return types so a wrong status is not representable. Each resolution step now returns a precise tagged value instead of None: identity resolution returns a _ResolvedKey or one of no_active_key / unavailable / unresolvable, classified the same way admission's _reload_admitted_key classifies the same conditions; upstream-lifetime classification returns a positive number of seconds, "unspecified" (absent or unparseable, which the envelope caps), or "expired" (a parseable non-positive value, an already-dead token); and upstream-grant validation returns a typed grant or one of no_access_token / expired_lifetime. Thin exhaustive mappers (match plus assert_never) lift each vocabulary into one bridge-mint taxonomy of eight named failures, and a single _bridge_mint_error_response gives each its truthful RFC 6749 §5.2 status: 400 for the caller's missing credential or an unsupported grant, 503 for a transient auth-DB outage, 500 for a gateway that cannot resolve identity or is not configured, and 502 for an upstream response with no usable token, an already-expired lifetime, or a token too large to seal. Adding a failure mode now requires a new literal and a match arm the type checker forces, so the class of wrong-status bug cannot recur silently. The refresh_token grant is rejected in _prepare_bridge_mint before the exchange with unsupported_grant_type, so it can never rotate or consume the client's upstream refresh credential; renewal is re-running authorization_code, as the sealed refresh_token=None already intends. An absent or unparseable expires_in still mints a capped envelope (the by-design behaviour for an upstream that omits the field); only an explicitly-dead lifetime is rejected. Tests cover the resolver's three failure classes (including a real connection-error outage and a missing prisma_client), the mint statuses for each (503 before the upstream exchange, 500, 502 on an expired upstream lifetime, and a capped mint on an unknown one), and the refresh-grant rejection before any exchange. The three findings are mutation-checked: reverting each fix turns its regression test red. --- .../mcp_server/discoverable_endpoints.py | 334 ++++++++++++------ .../mcp_server/test_discoverable_endpoints.py | 259 +++++++++++--- 2 files changed, 424 insertions(+), 169 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index abb375b5b6a..b8d10335182 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -377,74 +377,92 @@ def _active_key_user_id(key_obj: "UserAPIKeyAuth") -> str | None: return key_obj.user_id if _key_is_active(key_obj) else None -async def _resolve_active_litellm_key(request: Request) -> tuple[str, "UserAPIKeyAuth"] | None: - """Resolve the presented litellm key to ``(its hash, the live active key record)``, or ``None`` - when the key is absent, unresolvable, or blocked/expired. +@dataclass(frozen=True, slots=True) +class _ResolvedKey: + """An active litellm key resolved from the token request: its hash (the value ``get_key_object`` + and the cache/DB layer key the record by) and the live record.""" - Single resolution path the OAuth token endpoint reuses. Resolves authoritatively via - ``get_key_object`` (cache first, then DB) instead of a raw cache peek. On a multi-replica gateway - the token-exchange request can land on a worker whose in-memory cache never saw the key, and a - cross-replica Redis hit deserializes to a plain ``dict`` rather than a ``UserAPIKeyAuth``; the - previous code read only ``Authorization`` and did ``getattr(cached, "user_id")`` with no - ``model_type`` rehydration and no DB fallback, so it silently returned ``None``. The resolved key - is validated (``_key_is_active``) before it is trusted, so a blocked or expired key resolves to - ``None``, while a valid team-scoped or service-account key (no ``user_id``) still resolves so it - can mint a bridge envelope. The returned hash is the value ``get_key_object`` and the cache/DB - layer key the record by. Callers derive the ``user_id`` (per-user token store) or seal the hash - (dcr_bridge envelope) from the result. - """ + key_hash: str + key: "UserAPIKeyAuth" + + +_KeyResolutionFailure = Literal["no_active_key", "unavailable", "unresolvable"] +"""Why a token request yielded no active litellm key, kept distinct so a caller statuses each truthfully +instead of blaming the client for a gateway problem: +- ``no_active_key``: none was presented, or the presented key is unknown / blocked / expired (the + caller's request is at fault) +- ``unavailable``: the auth database was transiently unreachable while resolving (retryable) +- ``unresolvable``: the gateway cannot resolve identity right now (no DB connection, or an unexpected + error) -- a gateway fault, not the caller's +The classification mirrors admission's ``_reload_admitted_key`` so the mint (ingress) and admission +(egress) never disagree on the status of the same outage.""" + + +async def _resolve_active_litellm_key(request: Request) -> "_ResolvedKey | _KeyResolutionFailure": + """Resolve the presented litellm key to an active key record, or say precisely why not. + + Single resolution path the OAuth token endpoint reuses, resolving authoritatively via + ``get_key_object`` (cache first, then DB). The failure is a value, not a bare ``None``, so a caller + can tell "the client sent no usable credential" (a request error) apart from "the gateway could not + check" (an infrastructure error) and status each truthfully; collapsing both to ``None`` is what let + a DB outage read as a 400. A resolved key is still gated by ``_key_is_active``, so a blocked or + expired key is ``no_active_key`` while a valid team-scoped or service-account key (no ``user_id``) + resolves. Classification mirrors admission's ``_reload_admitted_key``: no DB connection is a gateway + fault, a ``ProxyException`` / ``HTTPException`` from ``get_key_object`` is an unknown or invalid key, + a database-service-unavailable error is a retryable outage, and anything else is an unexpected + gateway fault.""" token = _litellm_key_from_request(request) if not token: - return None - try: - from litellm.proxy._types import ( # noqa: PLC0415 # inline import avoids a module-load circular import - hash_token, - ) - from litellm.proxy.auth.auth_checks import ( # noqa: PLC0415 # inline import avoids a module-load circular import - get_key_object, - ) - from litellm.proxy.proxy_server import ( # noqa: PLC0415 # inline import avoids a module-load circular import - prisma_client, - user_api_key_cache, - ) + return "no_active_key" + from litellm.proxy._types import ( # noqa: PLC0415 # inline import avoids a module-load circular import + ProxyException, + hash_token, + ) + from litellm.proxy.auth.auth_checks import ( # noqa: PLC0415 # inline import avoids a module-load circular import + get_key_object, + ) + from litellm.proxy.db.exception_handler import ( # noqa: PLC0415 # inline import avoids a module-load circular import + PrismaDBExceptionHandler, + ) + from litellm.proxy.proxy_server import ( # noqa: PLC0415 # inline import avoids a module-load circular import + prisma_client, + user_api_key_cache, + ) - key_hash = hash_token(token) + if prisma_client is None: + return "unresolvable" + key_hash = hash_token(token) + try: key_obj = await get_key_object( hashed_token=key_hash, prisma_client=prisma_client, user_api_key_cache=user_api_key_cache, ) - except Exception as exc: # noqa: BLE001 # fail closed to None on any key-resolution error + except (ProxyException, HTTPException): + return "no_active_key" + except Exception as exc: # noqa: BLE001 # classify: a DB outage is retryable, anything else is an opaque gateway fault + if PrismaDBExceptionHandler.is_database_service_unavailable_error(exc): + return "unavailable" verbose_logger.debug( - "_resolve_active_litellm_key: could not resolve the presented key (%s)", + "_resolve_active_litellm_key: unexpected key-resolution error (%s)", type(exc).__name__, ) - return None + return "unresolvable" if not _key_is_active(key_obj): - return None - return key_hash, key_obj + return "no_active_key" + return _ResolvedKey(key_hash=key_hash, key=key_obj) async def _extract_user_id_from_request(request: Request) -> str | None: - """The LiteLLM ``user_id`` for the token request, so a per-user token is stored under the same - identity the egress later reads it by (``user_api_key_auth.user_id``). ``None`` when no active - key is present. See :func:`_resolve_active_litellm_key` for the resolution and active-key gate. - """ + """The litellm ``user_id`` for the token request, so a per-user token is stored under the same + identity the egress later reads it by. Storage is best-effort, so every non-resolved outcome + (including a transient DB outage) collapses to ``None`` here and the caller simply skips the store; + the bridge mint, which must status those outcomes differently, consumes + :func:`_resolve_active_litellm_key` directly.""" resolved = await _resolve_active_litellm_key(request) - return _active_key_user_id(resolved[1]) if resolved else None - - -async def _extract_active_key_hash_from_request(request: Request) -> str | None: - """The hash of the litellm key that authorized the token request, when it maps to an active key. - - A DCR-bridge envelope seals this hash so admission can reload the live ``UserAPIKeyAuth`` record - and enforce the key's current team/org/tool restrictions and revocation, rather than trusting a - frozen identity. The hash is a one-way digest, not a usable credential (the edge rejects a bare - hash presented as a bearer). ``None`` when no active key is present, so no envelope is minted for - a missing, unresolvable, or revoked key. - """ - resolved = await _resolve_active_litellm_key(request) - return resolved[0] if resolved else None + if not isinstance(resolved, _ResolvedKey): + return None + return _active_key_user_id(resolved.key) async def _store_per_user_token_server_side( @@ -713,38 +731,51 @@ async def authorize_with_server( return response -def _coerce_positive_expires_in(value: object) -> int | None: - """Coerce an upstream ``expires_in`` to a positive int, or ``None`` when it is absent or not a - usable number. IdPs return it as an int, a float (``3600.0``), or a numeric string (``"3600"``); - accepting only ``int`` would drop the float/string cases to ``None`` and fall back to the - envelope's 1h cap, which can outlive a shorter-lived upstream token and forward a stale bearer. - ``bool`` is excluded (it is an ``int`` subclass but never a real lifetime). Total over hostile - input: a non-numeric string, ``NaN``, ``Infinity``, or an over-large value all resolve to - ``None`` rather than raising (``int(float(...))`` can raise ``ValueError`` or ``OverflowError``), - so a malformed upstream ``expires_in`` never surfaces as a 500 from the token endpoint.""" - if isinstance(value, bool) or not isinstance(value, (int, float, str)): - return None +_UpstreamGrantRejection = Literal["no_access_token", "expired_lifetime"] +"""Why an upstream token response cannot back a bridge envelope: +- ``no_access_token``: the response carries no usable ``access_token`` +- ``expired_lifetime``: the response reports a parseable, non-positive ``expires_in``, i.e. an upstream + token that is already dead, so sealing it would forward a bearer the edge cannot use +An absent or unparseable ``expires_in`` is NOT a rejection; the lifetime is merely unknown and the +envelope caps it, the by-design behaviour for an upstream that omits the field.""" + + +def _classify_upstream_lifetime(raw_expires_in: object) -> "int | Literal['unspecified', 'expired']": + """Classify an upstream ``expires_in`` into a positive number of seconds, ``"unspecified"`` (absent + or unparseable, so the envelope caps it), or ``"expired"`` (a parseable non-positive value the + upstream reports as already elapsed). Telling "we do not know the lifetime" apart from "the upstream + says it is already dead" is what stops an explicitly-expired token from silently receiving the + envelope's 1h cap. ``bool`` is excluded (an ``int`` subclass but never a real lifetime), and + ``int(float(...))`` can raise on ``NaN`` / ``Infinity`` / oversized input, which reads as + unparseable rather than surfacing as a 500.""" + if raw_expires_in is None or isinstance(raw_expires_in, bool) or not isinstance(raw_expires_in, (int, float, str)): + return "unspecified" try: - seconds = int(float(value)) + seconds = int(float(raw_expires_in)) except (ValueError, TypeError, OverflowError): - return None - return seconds if seconds > 0 else None + return "unspecified" + return seconds if seconds > 0 else "expired" -def _bridge_grant_from_token_response(token_response: object) -> Optional["UpstreamTokenGrant"]: - """Validate an upstream OAuth token response into a typed grant, or None when it lacks a usable - access token. Each field is isinstance-checked so nothing untyped from ``response.json()`` flows - into the grant; ``expires_in`` is numerically coerced so a float/string lifetime is honored - rather than dropped to the envelope's default cap.""" +def _bridge_grant_from_token_response(token_response: object) -> "UpstreamTokenGrant | _UpstreamGrantRejection": + """Validate an upstream OAuth token response into a typed grant, or say why it cannot back an + envelope. Each field is isinstance-checked so nothing untyped from ``response.json()`` reaches the + grant. ``expires_in`` is read three ways (see :func:`_classify_upstream_lifetime`): an unknown + lifetime leaves the grant ``expires_in`` ``None`` for the envelope to cap, a positive value is + honoured, and an explicit already-elapsed value is a rejection rather than a silent fall-through to + the cap.""" from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import UpstreamTokenGrant, ) if not isinstance(token_response, dict): - return None + return "no_access_token" access = token_response.get("access_token") if not isinstance(access, str) or not access: - return None + return "no_access_token" + lifetime = _classify_upstream_lifetime(token_response.get("expires_in")) + if lifetime == "expired": + return "expired_lifetime" token_type = token_response.get("token_type") scope = token_response.get("scope") return UpstreamTokenGrant( @@ -756,7 +787,7 @@ def _bridge_grant_from_token_response(token_response: object) -> Optional["Upstr # follow-up (a dedicated refresh-envelope); the client re-runs authorization_code at the cap. refresh_token=None, scope=scope if isinstance(scope, str) and scope else None, - expires_in=_coerce_positive_expires_in(token_response.get("expires_in")), + expires_in=lifetime if isinstance(lifetime, int) else None, ) @@ -774,7 +805,16 @@ def _bridge_grant_from_token_response(token_response: object) -> Optional["Upstr # shape are uniform. Adding a failure mode is a new literal plus a match arm the type checker forces. # --------------------------------------------------------------------------- -_BridgeMintError = Literal["not_configured", "no_identity", "no_upstream_token", "too_large"] +_BridgeMintError = Literal[ + "no_identity", + "unsupported_grant", + "identity_unavailable", + "identity_unresolvable", + "not_configured", + "no_upstream_token", + "upstream_token_expired", + "too_large", +] @dataclass(frozen=True, slots=True) @@ -788,45 +828,105 @@ class _BridgeMintReady: def _bridge_mint_error_response(error: _BridgeMintError) -> JSONResponse: - """Map a bridge-mint failure value to its token-endpoint response. One place, RFC 6749 §5.2 shape - (top-level ``error``, no-store) for every case, with a status truthful about where the failure is: - the caller's request (400), the gateway config (500), or the upstream (502).""" - if error == "no_identity": - status, code, desc = ( - 400, - "invalid_request", - ( + """Map a bridge-mint failure value to its token-endpoint response: one place, RFC 6749 §5.2 shape + (top-level ``error``, no-store headers) for every case, with a status truthful about where the + failure is. The caller's request is 400, a transient gateway outage is 503, a gateway + misconfiguration is 500, and an upstream problem is 502. The identity-resolution statuses match how + admission statuses the same conditions on the egress side, so mint and admit never disagree under + one outage.""" + match error: + case "no_identity": + status, code, desc = ( + 400, + "invalid_request", "this server issues a gateway-bound credential; send a litellm credential " - "(x-litellm-api-key or Authorization) on the token request" - ), - ) - elif error == "not_configured": - status, code, desc = ( - 500, - "server_error", - ("the gateway is not configured to mint a gateway-bound credential (master_key is not set)"), - ) - elif error == "no_upstream_token": - status, code, desc = 502, "server_error", "the upstream token response has no usable access_token" - elif error == "too_large": - status, code, desc = ( - 502, - "server_error", - ("the upstream token is too large to seal into a gateway-bound credential"), - ) - else: - assert_never(error) + "(x-litellm-api-key or Authorization) on the token request", + ) + case "unsupported_grant": + status, code, desc = ( + 400, + "unsupported_grant_type", + "this server issues a gateway-bound credential and supports only the authorization_code " + "grant; re-run authorization_code to renew rather than refresh_token", + ) + case "identity_unavailable": + status, code, desc = ( + 503, + "temporarily_unavailable", + "the authentication database is temporarily unreachable; retry shortly", + ) + case "identity_unresolvable": + status, code, desc = ( + 500, + "server_error", + "the gateway could not resolve the litellm identity for this request", + ) + case "not_configured": + status, code, desc = ( + 500, + "server_error", + "the gateway is not configured to mint a gateway-bound credential (master_key is not set)", + ) + case "no_upstream_token": + status, code, desc = ( + 502, + "server_error", + "the upstream token response has no usable access_token", + ) + case "upstream_token_expired": + status, code, desc = ( + 502, + "server_error", + "the upstream token response reports an already-expired lifetime", + ) + case "too_large": + status, code, desc = ( + 502, + "server_error", + "the upstream token is too large to seal into a gateway-bound credential", + ) + case _: + assert_never(error) return JSONResponse( status_code=status, content={"error": code, "error_description": desc}, headers=TOKEN_NO_CACHE_HEADERS ) -async def _prepare_bridge_mint(request: Request) -> "_BridgeMintReady | _BridgeMintError": - """Phase 1, BEFORE the upstream exchange: validate that the gateway can mint (master_key set) and - that the request carries a resolvable litellm identity, and derive the envelope keys. Returns a - ready context or a failure value. Running before the exchange is what makes a missing master_key or - an unresolvable identity fail closed without consuming the single-use code / rotating a refresh - token, for both grant types.""" +def _key_resolution_failure_to_mint_error(failure: _KeyResolutionFailure) -> _BridgeMintError: + """Lift an identity-resolution failure into the mint taxonomy, preserving origin so the status stays + truthful: the caller's missing credential is 400, a transient DB outage is 503, and a gateway that + cannot resolve identity is 500.""" + match failure: + case "no_active_key": + return "no_identity" + case "unavailable": + return "identity_unavailable" + case "unresolvable": + return "identity_unresolvable" + case _: + assert_never(failure) + + +def _upstream_rejection_to_mint_error(rejection: _UpstreamGrantRejection) -> _BridgeMintError: + """Lift an upstream-response rejection into the mint taxonomy; both are upstream faults (502).""" + match rejection: + case "no_access_token": + return "no_upstream_token" + case "expired_lifetime": + return "upstream_token_expired" + case _: + assert_never(rejection) + + +async def _prepare_bridge_mint(request: Request, grant_type: str) -> "_BridgeMintReady | _BridgeMintError": + """Phase 1, BEFORE the upstream exchange: reject a grant this mint does not support, confirm the + gateway can mint (master_key set), resolve the litellm identity, and derive the envelope keys. + Returns a ready context or a precise failure value. Running before the exchange is what makes every + failure here fail closed without consuming the single-use code or rotating a refresh token. A bridge + server issues only envelopes and seals no upstream refresh_token, so the client holds none to + present: the refresh_token grant is rejected up front rather than exchanged (which could rotate the + upstream credential) and its result then discarded. Identity-resolution failures keep their origin + so the mapper statuses each truthfully.""" from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import envelope_keys_from_master_key, ) @@ -834,12 +934,14 @@ async def _prepare_bridge_mint(request: Request) -> "_BridgeMintReady | _BridgeM master_key, ) + if grant_type != "authorization_code": + return "unsupported_grant" if not master_key: return "not_configured" - key_hash = await _extract_active_key_hash_from_request(request) - if not key_hash: - return "no_identity" - return _BridgeMintReady(key_hash=key_hash, keys=envelope_keys_from_master_key(master_key)) + resolved = await _resolve_active_litellm_key(request) + if not isinstance(resolved, _ResolvedKey): + return _key_resolution_failure_to_mint_error(resolved) + return _BridgeMintReady(key_hash=resolved.key_hash, keys=envelope_keys_from_master_key(master_key)) def _finish_bridge_mint( @@ -848,18 +950,20 @@ def _finish_bridge_mint( """Phase 3, AFTER the upstream exchange: seal the upstream grant into the client-held envelope using the pre-resolved identity and keys, so the client holds one bearer that admits it and forwards the upstream token with nothing stored server-side. The only failures here are properties of the - upstream response (no usable token, or a token too large to seal), returned as values.""" + upstream response (no usable token, an already-expired lifetime, or a token too large to seal), + returned as values.""" from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import build_bridge_token_response, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import EnvelopeIdentity, SealedEnvelope, + UpstreamTokenGrant, ) grant = _bridge_grant_from_token_response(token_response) - if grant is None: - return "no_upstream_token" + if not isinstance(grant, UpstreamTokenGrant): + return _upstream_rejection_to_mint_error(grant) identity = EnvelopeIdentity(server_id=mcp_server.server_id, key_hash=ready.key_hash) sealed = build_bridge_token_response(identity, grant, ready.keys, now) if not isinstance(sealed, SealedEnvelope): @@ -949,7 +1053,7 @@ async def exchange_token_with_server( # phase 3. A failure here returns without ever touching the upstream credential. bridge_mint_ready: _BridgeMintReady | None = None if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: - prepared = await _prepare_bridge_mint(request) + prepared = await _prepare_bridge_mint(request, grant_type) if not isinstance(prepared, _BridgeMintReady): return _bridge_mint_error_response(prepared) bridge_mint_ready = prepared diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 4aa23247249..f611d2f6a24 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4367,6 +4367,7 @@ _BRIDGE_MASTER_KEY = "sk-bridge-producer-master-key-0123456789abcdef" async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_client_out=None): from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _ResolvedKey, exchange_token_with_server, ) @@ -4375,7 +4376,10 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_clie fake_http_response.raise_for_status = MagicMock() fake_http_client = MagicMock() fake_http_client.post = AsyncMock(return_value=fake_http_response) - key_resolver = AsyncMock(return_value=key_hash) + # The mint consumes _resolve_active_litellm_key's tagged result: an active key resolves to a + # _ResolvedKey carrying its hash; a request with no usable credential resolves to "no_active_key". + resolution = _ResolvedKey(key_hash=key_hash, key=MagicMock()) if key_hash is not None else "no_active_key" + key_resolver = AsyncMock(return_value=resolution) if fake_client_out is not None: fake_client_out["client"] = fake_http_client @@ -4385,7 +4389,7 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_clie return_value=fake_http_client, ), patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._resolve_active_litellm_key", new=key_resolver, ), patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), @@ -4511,10 +4515,12 @@ async def test_bridge_envelope_does_not_seal_upstream_refresh_token(): @pytest.mark.asyncio -async def test_bridge_refresh_grant_fails_closed_before_upstream_when_no_identity(): - """The pre-exchange identity gate covers the refresh_token grant, not just authorization_code: an - unresolvable litellm identity fails closed with invalid_request BEFORE the upstream refresh is - exchanged, so the client's refresh token is not rotated/consumed on a rejected request.""" +async def test_bridge_refresh_grant_is_rejected_before_upstream(): + """A bridge oauth_delegate server issues only envelopes and seals no upstream refresh_token, so the + client never holds one to present. _prepare_bridge_mint rejects the refresh_token grant up front + with unsupported_grant_type, BEFORE any upstream exchange, so a stray refresh request can never + rotate or consume the client's upstream refresh credential; renewal is re-running + authorization_code. This is checked before identity resolution, so it holds even with a valid key.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import exchange_token_with_server from litellm.types.mcp import MCPAuth @@ -4526,10 +4532,6 @@ async def test_bridge_refresh_grant_fails_closed_before_upstream_when_no_identit "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", return_value=fake_http_client, ), - patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", - new=AsyncMock(return_value=None), - ), patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), ): response = await exchange_token_with_server( @@ -4545,7 +4547,7 @@ async def test_bridge_refresh_grant_fails_closed_before_upstream_when_no_identit ) assert response.status_code == 400 - assert json.loads(response.body)["error"] == "invalid_request" + assert json.loads(response.body)["error"] == "unsupported_grant_type" fake_http_client.post.assert_not_called() @@ -4567,8 +4569,8 @@ async def test_bridge_mint_fails_closed_before_upstream_when_master_key_unset(): return_value=fake_http_client, ), patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_active_key_hash_from_request", - new=AsyncMock(return_value="hashed-litellm-key-77"), + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._resolve_active_litellm_key", + new=AsyncMock(return_value="no_active_key"), ), patch("litellm.proxy.proxy_server.master_key", None), ): @@ -4588,6 +4590,93 @@ async def test_bridge_mint_fails_closed_before_upstream_when_master_key_unset(): fake_http_client.post.assert_not_called() +async def _prepare_only_bridge_exchange(resolver_result): + """Drive exchange_token_with_server for a bridge oauth_delegate authorization_code request with the + identity resolver stubbed to a given tagged result, returning (response, post_mock) so a test can + assert the mapped status and that the single-use code was never exchanged.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import exchange_token_with_server + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock() + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._resolve_active_litellm_key", + new=AsyncMock(return_value=resolver_result), + ), + patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), + ): + response = await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri="https://claude.ai/api/mcp/auth_callback", + client_id="dcr-client-123", + client_secret=None, + code_verifier="verifier", + ) + return response, fake_http_client.post + + +@pytest.mark.asyncio +async def test_bridge_mint_db_outage_is_503_before_upstream(): + """A DB outage while resolving identity is a retryable gateway failure, so the mint returns 503 + temporarily_unavailable WITHOUT consuming the single-use code, matching how admission statuses the + same outage on the egress side. Collapsing every resolution failure to None used to blame the + client with 400 invalid_request for an infrastructure problem.""" + response, post = await _prepare_only_bridge_exchange("unavailable") + assert response.status_code == 503 + assert json.loads(response.body)["error"] == "temporarily_unavailable" + post.assert_not_called() + + +@pytest.mark.asyncio +async def test_bridge_mint_unresolvable_identity_is_500_before_upstream(): + """An unresolvable identity (no DB connection, or an unexpected resolution error) is a gateway + fault, so the mint returns 500 server_error before the exchange, a status distinct from both the + caller's 400 and the transient 503, matching admission's 500-vs-503 split for the same conditions.""" + response, post = await _prepare_only_bridge_exchange("unresolvable") + assert response.status_code == 500 + assert json.loads(response.body)["error"] == "server_error" + post.assert_not_called() + + +@pytest.mark.asyncio +async def test_bridge_mint_upstream_expired_lifetime_is_502(): + """An upstream token response reporting an already-elapsed lifetime (a parseable non-positive + expires_in) is rejected with 502 rather than sealed into an hour-long envelope around a dead + bearer. Regression for expires_in<=0 silently falling through to the 1h cap.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UP", "token_type": "Bearer", "expires_in": 0} + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + assert response.status_code == 502 + assert json.loads(response.body)["error"] == "server_error" + + +@pytest.mark.asyncio +async def test_bridge_mint_unknown_lifetime_is_capped_not_rejected(): + """An absent or unparseable expires_in leaves the lifetime unknown, which the envelope caps (never + inventing a longer life than the upstream stated); it is NOT rejected. Only an explicitly-dead + lifetime fails, so a metadata glitch on an otherwise-valid token still mints a bounded envelope.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UP", "token_type": "Bearer", "expires_in": "not-a-number"} + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + assert response.status_code == 200 + body = json.loads(response.body) + assert body["access_token"].startswith("llm_env_") + assert 0 < body["expires_in"] <= 3600 + + @pytest.mark.asyncio async def test_bridge_reported_expires_in_does_not_overstate_jwt_exp(): """The reported expires_in is derived from the envelope JWT's second-truncated exp (rounding the @@ -4638,34 +4727,51 @@ def test_bridge_reported_expires_in_can_be_zero_at_jwt_exp_boundary(): assert json.loads(response.body)["expires_in"] == 0 -def test_bridge_grant_coerces_numeric_expires_in(): - """expires_in from an IdP may be an int, a float (3600.0), or a numeric string ("3600"); coerce - it to a positive int so the envelope TTL honors the real lifetime instead of dropping a non-int - value and defaulting to the 1h cap (which can outlive a shorter-lived upstream token). bool and - non-numeric values become None.""" - from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( - _bridge_grant_from_token_response, - ) +def test_classify_upstream_lifetime(): + """expires_in from an IdP may be an int, a float (3600.0), or a numeric string ("3600"); each + coerces to a positive number of seconds. Absent or unparseable input (bool, non-numeric, NaN/inf, + oversized) is "unspecified" so the envelope caps it, while a parseable non-positive value is + "expired": the upstream reporting an already-dead token, which the mint must reject rather than + silently give the 1h cap.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import _classify_upstream_lifetime - def ei(v): - return _bridge_grant_from_token_response({"access_token": "x", "expires_in": v}).expires_in + assert _classify_upstream_lifetime(300) == 300 + assert _classify_upstream_lifetime(300.0) == 300 + assert _classify_upstream_lifetime("300") == 300 + assert _classify_upstream_lifetime(" 300 ") == 300 + # explicit, parseable, non-positive -> the upstream says the token is already dead + assert _classify_upstream_lifetime(0) == "expired" + assert _classify_upstream_lifetime(-5) == "expired" + # unknown lifetime -> cap (never invent a longer life than the upstream stated) + assert _classify_upstream_lifetime(None) == "unspecified" + assert _classify_upstream_lifetime(True) == "unspecified" + assert _classify_upstream_lifetime("nope") == "unspecified" + # hostile numerics must not raise (int(float(...)) can OverflowError) -> unspecified + assert _classify_upstream_lifetime("inf") == "unspecified" + assert _classify_upstream_lifetime("1e999") == "unspecified" + assert _classify_upstream_lifetime("-inf") == "unspecified" + assert _classify_upstream_lifetime("nan") == "unspecified" + assert _classify_upstream_lifetime(float("inf")) == "unspecified" + assert _classify_upstream_lifetime(10**400) == "unspecified" - assert ei(300) == 300 - assert ei(300.0) == 300 - assert ei("300") == 300 - assert ei(" 300 ") == 300 - assert ei(True) is None - assert ei("nope") is None - assert ei(0) is None - assert ei(-5) is None - assert ei(None) is None - # hostile numerics must not raise (int(float(...)) can OverflowError) -> None - assert ei("inf") is None - assert ei("1e999") is None - assert ei("-inf") is None - assert ei("nan") is None - assert ei(float("inf")) is None - assert ei(10**400) is None + +def test_bridge_grant_honors_and_rejects_upstream_lifetime(): + """The grant validator honors a positive lifetime, leaves an unknown one None for the envelope to + cap, and rejects an explicitly-expired one with "expired_lifetime" so a dead upstream token is + never sealed into an hour-long envelope.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import _bridge_grant_from_token_response + + def grant(v): + return _bridge_grant_from_token_response({"access_token": "x", "expires_in": v}) + + assert grant(300).expires_in == 300 + assert grant(120.0).expires_in == 120 + # unknown lifetime backs a grant whose expires_in the envelope caps; it is not a rejection + assert grant("nope").expires_in is None + assert _bridge_grant_from_token_response({"access_token": "x"}).expires_in is None + # an explicitly already-dead lifetime is rejected, not silently capped at 1h + assert grant(0) == "expired_lifetime" + assert grant(-5) == "expired_lifetime" @pytest.mark.asyncio @@ -5073,13 +5179,14 @@ async def test_extract_user_id_rejects_expired_key(proxy_globals): @pytest.mark.asyncio -async def test_extract_active_key_hash_returns_hash_for_active_key(proxy_globals): +async def test_resolve_active_litellm_key_returns_resolved_key_for_active_key(proxy_globals): """The dcr_bridge mint seals the hash of the authorizing key so admission can reload the live record. For an active key the resolver returns exactly hash_token(key), the same value get_key_object and the whole cache/DB layer key the record by, so the sealed reference resolves back to this key at admission.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( - _extract_active_key_hash_from_request, + _resolve_active_litellm_key, + _ResolvedKey, ) from litellm.proxy._types import UserAPIKeyAuth, hash_token from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache @@ -5095,19 +5202,22 @@ async def test_extract_active_key_hash_returns_hash_for_active_key(proxy_globals proxy_globals.prisma_client = object() request = _token_request({"x-litellm-api-key": f"Bearer {key}"}) - assert await _extract_active_key_hash_from_request(request) == hash_token(key) + resolved = await _resolve_active_litellm_key(request) + assert isinstance(resolved, _ResolvedKey) + assert resolved.key_hash == hash_token(key) @pytest.mark.asyncio -async def test_extract_active_key_hash_returns_hash_for_active_key_without_user_id(proxy_globals): +async def test_resolve_active_litellm_key_resolves_key_without_user_id(proxy_globals): """A valid team-scoped or service-account key has no user_id but is a legitimate credential, so it must still resolve to a hash and be able to mint a bridge envelope. Gating the resolver on user_id presence wrongly rejected these keys with invalid_request; the active-state gate now checks only blocked and expiry, and the key hash (not the user) is what the mint seals. The per-user token store still gets no user for such a key, since there is none to key a stored credential by.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( - _extract_active_key_hash_from_request, _extract_user_id_from_request, + _resolve_active_litellm_key, + _ResolvedKey, ) from litellm.proxy._types import UserAPIKeyAuth, hash_token from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache @@ -5123,16 +5233,18 @@ async def test_extract_active_key_hash_returns_hash_for_active_key_without_user_ proxy_globals.prisma_client = object() request = _token_request({"x-litellm-api-key": f"Bearer {key}"}) - assert await _extract_active_key_hash_from_request(request) == hash_token(key) + resolved = await _resolve_active_litellm_key(request) + assert isinstance(resolved, _ResolvedKey) + assert resolved.key_hash == hash_token(key) assert await _extract_user_id_from_request(request) is None @pytest.mark.asyncio -async def test_extract_active_key_hash_rejects_blocked_key(proxy_globals): +async def test_resolve_active_litellm_key_rejects_blocked_key(proxy_globals): """A blocked key must not yield a hash, so no gateway-bound envelope is minted for a revoked key; the mint fails closed with invalid_request instead.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( - _extract_active_key_hash_from_request, + _resolve_active_litellm_key, ) from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache @@ -5145,17 +5257,17 @@ async def test_extract_active_key_hash_rejects_blocked_key(proxy_globals): proxy_globals.prisma_client = _FakePrisma() request = _token_request({"x-litellm-api-key": "sk-blocked-key"}) - assert await _extract_active_key_hash_from_request(request) is None + assert await _resolve_active_litellm_key(request) == "no_active_key" @pytest.mark.asyncio -async def test_extract_active_key_hash_fails_closed_on_malformed_expiry(proxy_globals): +async def test_resolve_active_litellm_key_fails_closed_on_malformed_expiry(proxy_globals): """A key whose stored expires string does not parse must fail closed to no-hash (the mint then returns invalid_request), not surface an unhandled 500. The active-state check runs outside the resolver's try, so it must be total over a bad expires rather than letting datetime.fromisoformat raise. Before the fix this raised a ValueError instead of returning None.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( - _extract_active_key_hash_from_request, + _resolve_active_litellm_key, ) from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache @@ -5168,14 +5280,14 @@ async def test_extract_active_key_hash_fails_closed_on_malformed_expiry(proxy_gl proxy_globals.prisma_client = _FakePrisma() request = _token_request({"x-litellm-api-key": "sk-bad-expiry-key"}) - assert await _extract_active_key_hash_from_request(request) is None + assert await _resolve_active_litellm_key(request) == "no_active_key" @pytest.mark.asyncio -async def test_extract_active_key_hash_none_without_litellm_key(proxy_globals): +async def test_resolve_active_litellm_key_no_active_key_without_litellm_key(proxy_globals): """No LiteLLM key on the request yields no hash without consulting the resolver.""" from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( - _extract_active_key_hash_from_request, + _resolve_active_litellm_key, ) from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache @@ -5183,7 +5295,46 @@ async def test_extract_active_key_hash_none_without_litellm_key(proxy_globals): proxy_globals.prisma_client = object() request = _token_request({"content-type": "application/json"}) - assert await _extract_active_key_hash_from_request(request) is None + assert await _resolve_active_litellm_key(request) == "no_active_key" + + +@pytest.mark.asyncio +async def test_resolve_active_litellm_key_db_outage_is_unavailable(proxy_globals): + """A database outage while resolving the presented key is a retryable infrastructure failure, not + the caller's fault, so the resolver reports "unavailable" (the mint statuses it 503) rather than + collapsing it to the same value as a missing credential. is_database_service_unavailable_error + classifies a connection error (an OSError) as an outage, matching admission's egress-side handling.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _resolve_active_litellm_key, + ) + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + class _OutagePrisma: + async def get_data(self, token, table_name, parent_otel_span=None, proxy_logging_obj=None): + raise ConnectionError("connection refused") + + proxy_globals.user_api_key_cache = UserApiKeyCache() + proxy_globals.prisma_client = _OutagePrisma() + + request = _token_request({"x-litellm-api-key": "sk-during-outage"}) + assert await _resolve_active_litellm_key(request) == "unavailable" + + +@pytest.mark.asyncio +async def test_resolve_active_litellm_key_no_database_is_unresolvable(proxy_globals): + """With no database connection configured the gateway cannot verify the presented key at all, so + the resolver reports "unresolvable" (the mint statuses it 500) instead of blaming the caller. + Mirrors admission, which 500s a missing prisma_client on the egress side.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _resolve_active_litellm_key, + ) + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + proxy_globals.user_api_key_cache = UserApiKeyCache() + proxy_globals.prisma_client = None + + request = _token_request({"x-litellm-api-key": "sk-no-db"}) + assert await _resolve_active_litellm_key(request) == "unresolvable" @pytest.mark.asyncio From 55ff3a242c269228dc1d54d0ffaffa40fb66ca5d Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 18:39:00 -0700 Subject: [PATCH 302/399] fix(mcp): treat a positive sub-second upstream lifetime as alive, not expired _classify_upstream_lifetime decided "expired" from int(float(expires_in)), which truncates toward zero, so a positive fractional lifetime in (0, 1) became 0 and was misread as already elapsed. That rejected the mint with 502 in _finish_bridge_mint after the single-use upstream code had already been consumed, even though the upstream reported a positive remaining lifetime. Decide expired on the parsed numeric value rather than its truncated int, so only a genuinely non-positive value is expired. The envelope works in whole seconds and cannot represent a sub-second lifetime, so a positive value that truncates to 0 clamps up to the 1s floor instead of being rejected. Values >= 1 still truncate toward zero so the envelope never claims more life than the upstream stated, and NaN / Infinity / oversized input still read as unparseable ("unspecified"). Regression covers the classifier (0.5 and 0.001 clamp to 1, 1.9 truncates to 1, -0.5 stays expired) and the mint (a 0.5s upstream lifetime mints a 200 envelope rather than a 502); reverting to the truncate-then-check reddens both. --- .../mcp_server/discoverable_endpoints.py | 21 ++++++++++------- .../mcp_server/test_discoverable_endpoints.py | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index b8d10335182..8d1713a5911 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -742,19 +742,24 @@ envelope caps it, the by-design behaviour for an upstream that omits the field." def _classify_upstream_lifetime(raw_expires_in: object) -> "int | Literal['unspecified', 'expired']": """Classify an upstream ``expires_in`` into a positive number of seconds, ``"unspecified"`` (absent - or unparseable, so the envelope caps it), or ``"expired"`` (a parseable non-positive value the - upstream reports as already elapsed). Telling "we do not know the lifetime" apart from "the upstream - says it is already dead" is what stops an explicitly-expired token from silently receiving the - envelope's 1h cap. ``bool`` is excluded (an ``int`` subclass but never a real lifetime), and - ``int(float(...))`` can raise on ``NaN`` / ``Infinity`` / oversized input, which reads as - unparseable rather than surfacing as a 500.""" + or unparseable, so the envelope caps it), or ``"expired"`` (a non-positive value the upstream reports + as already elapsed). Telling "we do not know the lifetime" apart from "the upstream says it is + already dead" is what stops an explicitly-expired token from silently receiving the envelope's 1h + cap. The expired decision is made on the parsed numeric value, not on ``int(...)`` of it, so a + positive sub-second lifetime in ``(0, 1)`` is not truncated to ``0`` and misread as elapsed; the + envelope works in whole seconds, so such a lifetime clamps up to its 1s floor. ``bool`` is excluded + (an ``int`` subclass but never a real lifetime), and the conversions can raise on ``NaN`` / + ``Infinity`` / oversized input, which reads as unparseable rather than surfacing as a 500.""" if raw_expires_in is None or isinstance(raw_expires_in, bool) or not isinstance(raw_expires_in, (int, float, str)): return "unspecified" try: - seconds = int(float(raw_expires_in)) + numeric = float(raw_expires_in) + seconds = int(numeric) except (ValueError, TypeError, OverflowError): return "unspecified" - return seconds if seconds > 0 else "expired" + if numeric <= 0: + return "expired" + return max(1, seconds) def _bridge_grant_from_token_response(token_response: object) -> "UpstreamTokenGrant | _UpstreamGrantRejection": diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index f611d2f6a24..68466e624ec 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4661,6 +4661,22 @@ async def test_bridge_mint_upstream_expired_lifetime_is_502(): assert json.loads(response.body)["error"] == "server_error" +@pytest.mark.asyncio +async def test_bridge_mint_positive_sub_second_lifetime_mints_not_502(): + """A positive fractional expires_in in (0, 1) is a live token, not an elapsed one, so it mints a + (1s-floored) envelope rather than being truncated to 0 and rejected with 502 after the single-use + code was already consumed. Regression for classifying a sub-second remaining lifetime as expired.""" + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + upstream = {"access_token": "UP", "token_type": "Bearer", "expires_in": 0.5} + response = await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77") + assert response.status_code == 200 + body = json.loads(response.body) + assert body["access_token"].startswith("llm_env_") + assert body["expires_in"] >= 0 + + @pytest.mark.asyncio async def test_bridge_mint_unknown_lifetime_is_capped_not_rejected(): """An absent or unparseable expires_in leaves the lifetime unknown, which the envelope caps (never @@ -4742,6 +4758,13 @@ def test_classify_upstream_lifetime(): # explicit, parseable, non-positive -> the upstream says the token is already dead assert _classify_upstream_lifetime(0) == "expired" assert _classify_upstream_lifetime(-5) == "expired" + assert _classify_upstream_lifetime(-0.5) == "expired" + # a positive sub-second lifetime is alive, not elapsed; it clamps up to the envelope's 1s floor + # rather than truncating to 0 and being misread as expired + assert _classify_upstream_lifetime(0.5) == 1 + assert _classify_upstream_lifetime(0.001) == 1 + # a positive value >= 1 truncates toward zero (never overstating the stated lifetime) + assert _classify_upstream_lifetime(1.9) == 1 # unknown lifetime -> cap (never invent a longer life than the upstream stated) assert _classify_upstream_lifetime(None) == "unspecified" assert _classify_upstream_lifetime(True) == "unspecified" From 0e90f61e48a84ae8fd7610333032a40b22e6c928 Mon Sep 17 00:00:00 2001 From: Abhimanyu Kapur <38531241+akapur99@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:21:51 -0700 Subject: [PATCH 303/399] fix(auto_router): inline error for missing LLM classifier model Selecting the LLM classifier without picking a model only surfaced a toast on submit; the classifier model select now gets the same red outline and helper text as the tier and embedding selects once a submit attempt has failed. --- .../add_model/ComplexityRouterConfig.test.tsx | 22 +++++++++++++++++++ .../add_model/ComplexityRouterConfig.tsx | 9 ++++++++ 2 files changed, 31 insertions(+) diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx index 0613b0c02ae..a34a8709918 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx @@ -226,6 +226,28 @@ describe("ComplexityRouterConfig", () => { expect(screen.queryByText("This tier is required")).not.toBeInTheDocument(); }); + it("shows an inline error on the classifier model select when llm is selected without a model", () => { + const llmValue: ComplexityRouterConfigValue = { + ...defaultValue, + classifier_type: "llm", + classifier_llm_config: { model: "", timeout_ms: 3000 }, + }; + renderWithProviders(); + fireEvent.click(screen.getByText("Advanced: Classification Method")); + expect(screen.getByText("A classifier model is required")).toBeInTheDocument(); + }); + + it("does not show the classifier model error once a classifier model is set", () => { + const llmValue: ComplexityRouterConfigValue = { + ...defaultValue, + classifier_type: "llm", + classifier_llm_config: { model: "gpt-3.5-turbo", timeout_ms: 3000 }, + }; + renderWithProviders(); + fireEvent.click(screen.getByText("Advanced: Classification Method")); + expect(screen.queryByText("A classifier model is required")).not.toBeInTheDocument(); + }); + it("shows a validation error only under unfilled tiers when showValidationErrors is true", () => { renderWithProviders( = ({ label: model.model_group, })); + const classifierModelMissing = + showValidationErrors && value.classifier_type === "llm" && !value.classifier_llm_config?.model; + const handleTierChange = (tier: keyof ComplexityTiers, model: string) => { onChange({ ...value, @@ -233,7 +236,13 @@ const ComplexityRouterConfig: React.FC = ({ showSearch style={{ width: "100%" }} options={modelOptions} + status={classifierModelMissing ? "error" : undefined} /> + {classifierModelMissing && ( + + A classifier model is required + + )}
From c9beaf85ff9713371ff5a20154a71429aeac8017 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:25:39 -0700 Subject: [PATCH 304/399] build(dev-env): add make bootstrap and unprovisioned-checkout preflight to pre-commit --- CLAUDE.md | 2 ++ Makefile | 15 ++++++++++++++- scripts/pre_commit_lint.sh | 23 +++++++++++++++++++++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 78da2c65d96..0c679e92113 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,6 +39,8 @@ Don't hesitate to use values in .env to get needed API keys and other secrets, a Python max line length is 120, not 88 +On a fresh worktree or clone, run `make bootstrap` before anything else. It provisions everything tests, `make pre-commit`, and a local proxy need: the uv env with proxy extras, the Prisma client, and the dashboard's node_modules; on worktrees it also copies `.env` from the main checkout (it never overwrites an existing `.env`) + Run tests before you commit. Also, run `make pre-commit` right before each commit, which generates types (as needed) and formats/lints your code. Any errors found must be fixed. It only runs when there are staged frontend and/or backend changes and calculates violations, generates types, etc. based on the worktree, so stage what you need or stash/delete unwanted files in litellm/ or ui/ (where backend and frontend lint run, respectively) before running it. If it fails because dashboard api types are stale, it already regenerated them for you. You just need to stage the schema.d.ts, re-run `make pre-commit` to confirm it passes, and commit When you fix violations gated by `ruff-strict-budget.json`, `type-discipline-budget.json`, or `basedpyright-code-budget.json`, run `make lint-budget-update` and commit the lowered limits so the ceilings ratchet down instead of leaving stale headroom. It measures the working tree, so it must contain exactly the fixes you're committing diff --git a/Makefile b/Makefile index 965a3254616..d035f1703bd 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,12 @@ lint-ruff-budget lint-ruff-budget-update lint-budget-update lint-gate \ install-dev install-proxy-dev install-test-deps install-hooks \ install-helm-unittest check-circular-imports check-import-safety pre-commit \ - lint-install lint-fetch-base + lint-install lint-fetch-base bootstrap # Default target help: @echo "Available commands:" + @echo " make bootstrap - Provision a fresh clone/worktree: Python env with proxy extras, Prisma client, dashboard node_modules; worktrees also copy .env from the main checkout" @echo " make install-dev - Install development dependencies" @echo " make install-proxy-dev - Install proxy development dependencies" @echo " make install-dev-ci - Install dev dependencies (CI-compatible, pins OpenAI)" @@ -71,6 +72,18 @@ info: install-dev: $(UV) sync --inexact --frozen +bootstrap: + $(UV) sync --inexact --frozen --extra proxy --group proxy-dev --group e2e-dev + $(UV_RUN) python scripts/prisma_generate_if_needed.py + cd ui/litellm-dashboard && npm ci --no-audit --no-fund + @main_root=$$(git worktree list --porcelain | head -1 | sed 's/^worktree //'); \ + if [ "$$main_root" != "$$(git rev-parse --show-toplevel)" ] && [ -f "$$main_root/.env" ] && [ ! -f .env ]; then \ + cp "$$main_root/.env" .env && echo "bootstrap: copied .env from $$main_root"; \ + else \ + echo "bootstrap: .env left untouched"; \ + fi + @echo "bootstrap: done" + install-proxy-dev: $(UV) sync --frozen --group proxy-dev --extra proxy diff --git a/scripts/pre_commit_lint.sh b/scripts/pre_commit_lint.sh index d7d560ce947..cce0cb61c1e 100755 --- a/scripts/pre_commit_lint.sh +++ b/scripts/pre_commit_lint.sh @@ -89,6 +89,11 @@ EOF status=0 +bootstrap_hint() { + echo " This checkout looks unprovisioned (fresh worktree or clone)." >&2 + echo " Fix: make bootstrap" >&2 +} + if [ -n "$litellm_py_files" ]; then echo "pre-commit: linting Python (make lint)" make lint || { echo "✗ Python lint failed. Fix the reds above, then re-run make pre-commit." >&2; status=1; } @@ -109,7 +114,13 @@ fi if [ -n "$ui_prettier_files" ] || [ -n "$ui_eslint_files" ]; then echo "pre-commit: linting dashboard (prettier + eslint + lint budgets)" - lint_dashboard || { echo "✗ Dashboard lint failed. See above; format with: (cd ui/litellm-dashboard && npm run format)." >&2; status=1; } + if [ ! -d ui/litellm-dashboard/node_modules ]; then + echo "✗ ui/litellm-dashboard/node_modules is missing; dashboard lint cannot run." >&2 + bootstrap_hint + status=1 + else + lint_dashboard || { echo "✗ Dashboard lint failed. See above; format with: (cd ui/litellm-dashboard && npm run format)." >&2; status=1; } + fi fi if [ -n "$spec_files" ]; then @@ -118,7 +129,15 @@ if [ -n "$spec_files" ]; then # and an up-to-date Prisma client; check-ui-api-types.yml installs those and runs # prisma generate before gen:api, so mirror that here or a stale client can mask # drift that CI will still flag. - if ! uv run --no-sync python scripts/prisma_generate_if_needed.py; then + if [ ! -d ui/litellm-dashboard/node_modules ]; then + echo "✗ ui/litellm-dashboard/node_modules is missing; the gen:api sync check cannot run." >&2 + bootstrap_hint + status=1 + elif ! uv run --no-sync python -c "import orjson, prisma" 2>/dev/null; then + echo "✗ The Python env lacks the proxy deps (orjson/prisma) that gen:api needs." >&2 + bootstrap_hint + status=1 + elif ! uv run --no-sync python scripts/prisma_generate_if_needed.py; then echo "✗ Could not regenerate Prisma client (prisma generate failed)." >&2 status=1 elif ( cd ui/litellm-dashboard && LITELLM_PYTHON="uv run --no-sync python" npm run gen:api ); then From f717e3b2f0d4914ee5311f058a2514d676306988 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 11 Jul 2026 19:27:11 -0700 Subject: [PATCH 305/399] feat(router): random-pick multi-model complexity tiers (#32967) * feat(router): random-pick multi-model complexity tiers Tier pools already make sense without adaptive; stop pinning lists to index 0 and shuffle within the classified tier instead. Co-authored-by: Cursor * fix(ci): format complexity router config Co-authored-by: Cursor * fix(ci): use PEP 585 types for tier pools Co-authored-by: Cursor --------- Co-authored-by: Cursor --- .../complexity_router/complexity_router.py | 23 ++++++++------ .../complexity_router/config.py | 25 +++++++++++++--- .../router_strategy/test_complexity_router.py | 30 +++++++++++++++++++ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 11719b8a18f..2138a0112a0 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -14,6 +14,7 @@ Inspired by ClawRouter: https://github.com/BlockRunAI/ClawRouter """ import asyncio +import random import re from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast @@ -437,22 +438,26 @@ class ComplexityRouter(CustomLogger): """ tier_key = tier.value if isinstance(tier, ComplexityTier) else tier - # Check config tiers mapping - model = self.config.tiers.get(tier_key) - if model: - return model + if tier_key in self.config.tiers: + return self._pick_from_tier_value(self.config.tiers[tier_key], tier_key) - # Fallback to default model if configured if self.config.default_model: return self.config.default_model - # Last resort: return MEDIUM tier model or error - medium_model = self.config.tiers.get(ComplexityTier.MEDIUM.value) - if medium_model: - return medium_model + medium_key = ComplexityTier.MEDIUM.value + if medium_key in self.config.tiers: + return self._pick_from_tier_value(self.config.tiers[medium_key], medium_key) raise ValueError(f"No model configured for tier {tier_key} and no default_model set") + @staticmethod + def _pick_from_tier_value(model: str | list[str], tier_key: str) -> str: + if isinstance(model, str): + return model + if not model: + raise ValueError(f"Empty model pool for tier {tier_key}") + return random.choice(model) + def _lexical_tier_override(self, user_message: str) -> Optional[ComplexityTier]: """When keyword_tier_rules match literally, the most-severe matched tier wins. diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 125de6f7489..8c8e5acb51f 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -8,7 +8,7 @@ All values are configurable via proxy config.yaml. from enum import Enum from typing import Dict, List, Literal, Optional -from pydantic import BaseModel, ConfigDict, Field, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator class ComplexityTier(str, Enum): @@ -244,10 +244,12 @@ class ClassifierLLMConfig(BaseModel): class ComplexityRouterConfig(BaseModel): """Configuration for the ComplexityRouter.""" - # Tier to model mapping - tiers: Dict[str, str] = Field( + # string = pin; list = random pick from the tier pool + tiers: dict[str, str | list[str]] = Field( default_factory=lambda: DEFAULT_TIER_MODELS.copy(), - description="Mapping of complexity tiers to model names", + description=( + "Mapping of complexity tiers to a model or model pool. A list is randomly picked from for that tier" + ), ) # Tier boundaries (normalized scores) @@ -335,6 +337,21 @@ class ComplexityRouterConfig(BaseModel): model_config = ConfigDict(extra="allow") # Allow additional fields + @field_validator("tiers", mode="before") + @classmethod + def _coerce_tier_values(cls, value: object) -> object: + if not isinstance(value, dict): + return value + coerced: dict[str, object] = {} + for key, item in value.items(): + if isinstance(item, str): + coerced[key] = item + elif isinstance(item, (list, tuple)): + coerced[key] = list(item) + else: + coerced[key] = item + return coerced + @model_validator(mode="after") def _validate_llm_classifier_config(self) -> "ComplexityRouterConfig": if self.classifier_type == "llm" and self.classifier_llm_config is None: diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index f47c19b2baa..e1133620a57 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -315,6 +315,36 @@ class TestModelSelection: model = router.get_model_for_tier(ComplexityTier.SIMPLE) assert model == "fallback-model" + def test_get_model_for_tier_list_random_choice(self, mock_router_instance): + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "tiers": {"SIMPLE": ["cheap", "premium"], "MEDIUM": "mid"}, + "default_model": "mid", + }, + ) + pool = ["cheap", "premium"] + with patch( + "litellm.router_strategy.complexity_router.complexity_router.random.choice", + return_value="premium", + ) as choice: + assert router.get_model_for_tier(ComplexityTier.SIMPLE) == "premium" + choice.assert_called_once_with(pool) + assert router.get_model_for_tier(ComplexityTier.MEDIUM) == "mid" + + def test_get_model_for_tier_empty_pool_raises(self, mock_router_instance): + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "tiers": {"SIMPLE": []}, + "default_model": "mid", + }, + ) + with pytest.raises(ValueError, match="Empty model pool for tier SIMPLE"): + router.get_model_for_tier(ComplexityTier.SIMPLE) + class TestPreRoutingHook: """Test the async_pre_routing_hook method.""" From f61fd2fb6d5dfd07850cb0ae1a486a6b9adcb18b Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Sat, 11 Jul 2026 19:46:02 -0700 Subject: [PATCH 306/399] fix(xecguard): sanitize scan result before recording it for logging (#32935) --- .../guardrail_hooks/xecguard/xecguard.py | 11 +++++++- .../guardrail_hooks/test_xecguard.py | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py b/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py index f4a6f0aeb3b..7fe942bcb38 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py +++ b/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py @@ -44,6 +44,8 @@ from litellm.integrations.custom_guardrail import ( CustomGuardrail, log_guardrail_information, ) +from litellm.litellm_core_utils.core_helpers import redact_nested_match_and_regex_keys +from litellm.litellm_core_utils.sensitive_data_masker import mask_credentials_in_payload from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, @@ -64,6 +66,13 @@ if TYPE_CHECKING: ) +def _sanitize_scan_result_for_logging(scan_result: dict) -> dict: + without_secrets = {key: value for key, value in scan_result.items() if key != "secret_fields"} + redacted = redact_nested_match_and_regex_keys(without_secrets) + masked = mask_credentials_in_payload(redacted if isinstance(redacted, dict) else without_secrets) + return masked if isinstance(masked, dict) else without_secrets + + _DEFAULT_API_BASE = "https://api-xecguard.cycraft.ai" _SCAN_ENDPOINT = "/xecguard/v1/scan" _GROUNDING_ENDPOINT = "/xecguard/v1/grounding" @@ -253,7 +262,7 @@ class XecGuardGuardrail(CustomGuardrail): slg = StandardLoggingGuardrailInformation( guardrail_name=self.guardrail_name or "xecguard", guardrail_mode=GuardrailEventHooks.logging_only, - guardrail_response=scan_result, + guardrail_response=_sanitize_scan_result_for_logging(scan_result), guardrail_status=guardrail_status, start_time=start_time.timestamp(), end_time=end_time.timestamp(), diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py index f64967abbb0..6e601df897b 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py @@ -1675,6 +1675,33 @@ class TestXecGuardLoggingHook: assert info_list[1]["guardrail_name"] == "test-xecguard" assert info_list[1]["guardrail_response"]["trace_id"] == "lg-4" + @pytest.mark.asyncio + async def test_async_logging_hook_sanitizes_scan_result( + self, xecguard_guardrail, mock_request_data + ): + resp = _make_response( + { + "decision": "SAFE", + "trace_id": "lg-5", + "secret_fields": {"authorization": "Bearer xgs_raw"}, + "detections": [{"match": "raw matched span", "policy": "pii"}], + "api_key": "xgs_super_secret_value", + } + ) + with patch.object(xecguard_guardrail.async_handler, "post", return_value=resp): + kwargs = {**mock_request_data, "standard_logging_object": {}} + await xecguard_guardrail.async_logging_hook( + kwargs=kwargs, + result=_build_model_response("some answer"), + call_type="acompletion", + ) + info = kwargs["standard_logging_object"]["guardrail_information"][0] + guardrail_response = info["guardrail_response"] + assert "secret_fields" not in guardrail_response + assert guardrail_response["detections"][0]["match"] == "[REDACTED]" + assert guardrail_response["api_key"] != "xgs_super_secret_value" + assert guardrail_response["trace_id"] == "lg-5" + @pytest.mark.asyncio async def test_async_logging_hook_without_response_records_info( self, xecguard_guardrail, mock_request_data From 1bff68c0ced29c926212e33e260d9ccdcb76a88a Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 20:23:41 -0700 Subject: [PATCH 307/399] chore: keep it brief --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0c679e92113..9f708716c6d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,7 @@ Don't hesitate to use values in .env to get needed API keys and other secrets, a Python max line length is 120, not 88 -On a fresh worktree or clone, run `make bootstrap` before anything else. It provisions everything tests, `make pre-commit`, and a local proxy need: the uv env with proxy extras, the Prisma client, and the dashboard's node_modules; on worktrees it also copies `.env` from the main checkout (it never overwrites an existing `.env`) +On a fresh worktree or clone, run `make bootstrap` before anything else. It provisions everything tests, `make pre-commit`, and a local proxy need Run tests before you commit. Also, run `make pre-commit` right before each commit, which generates types (as needed) and formats/lints your code. Any errors found must be fixed. It only runs when there are staged frontend and/or backend changes and calculates violations, generates types, etc. based on the worktree, so stage what you need or stash/delete unwanted files in litellm/ or ui/ (where backend and frontend lint run, respectively) before running it. If it fails because dashboard api types are stale, it already regenerated them for you. You just need to stage the schema.d.ts, re-run `make pre-commit` to confirm it passes, and commit From 732c382644f714f3e7f5be1d454d351e9cc17a5b Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 20:25:53 -0700 Subject: [PATCH 308/399] chore: keep it brief --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d035f1703bd..8b657dcb465 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # Default target help: @echo "Available commands:" - @echo " make bootstrap - Provision a fresh clone/worktree: Python env with proxy extras, Prisma client, dashboard node_modules; worktrees also copy .env from the main checkout" + @echo " make bootstrap - Provision a fresh clone/worktree" @echo " make install-dev - Install development dependencies" @echo " make install-proxy-dev - Install proxy development dependencies" @echo " make install-dev-ci - Install dev dependencies (CI-compatible, pins OpenAI)" From 6401908f65b8bd2ed18f33a38fab843ee5fea184 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 20:29:16 -0700 Subject: [PATCH 309/399] docs(readme): point developer-mode setup at make bootstrap --- README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 90d3e944fcc..0e6038a9e4b 100644 --- a/README.md +++ b/README.md @@ -552,17 +552,12 @@ The Terraform modules live at [`terraform/litellm/aws/`](./terraform/litellm/aws 2. Run dependent services `docker-compose up db prometheus` #### Backend -1. (In root) create virtual environment `python -m venv .venv` -2. Activate virtual environment `source .venv/bin/activate` -3. Install dependencies `uv sync --all-extras --group proxy-dev` -4. `uv run prisma generate` -5. `prisma generate` -6. Start proxy backend `python litellm/proxy/proxy_cli.py` +1. (In root) provision the checkout with `make bootstrap` (installs the Python env with proxy extras, generates the Prisma client, and installs the dashboard's node_modules) +2. Start proxy backend `uv run python litellm/proxy/proxy_cli.py` #### Frontend -1. Navigate to `ui/litellm-dashboard` -2. Install dependencies `npm install` -3. Run `npm run dev` to start the dashboard +1. Navigate to `ui/litellm-dashboard` (dependencies were already installed by `make bootstrap`) +2. Run `npm run dev` to start the dashboard ### Verify Docker Image Signatures From a523895a573f0ed7867e15b8f1fa6e975d690a25 Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 11 Jul 2026 20:32:34 -0700 Subject: [PATCH 310/399] chore: keep it concise --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0e6038a9e4b..32b0160dbaa 100644 --- a/README.md +++ b/README.md @@ -552,12 +552,12 @@ The Terraform modules live at [`terraform/litellm/aws/`](./terraform/litellm/aws 2. Run dependent services `docker-compose up db prometheus` #### Backend -1. (In root) provision the checkout with `make bootstrap` (installs the Python env with proxy extras, generates the Prisma client, and installs the dashboard's node_modules) -2. Start proxy backend `uv run python litellm/proxy/proxy_cli.py` +1. Run `make bootstrap` +2. Start proxy backend: `uv run python litellm/proxy/proxy_cli.py` #### Frontend -1. Navigate to `ui/litellm-dashboard` (dependencies were already installed by `make bootstrap`) -2. Run `npm run dev` to start the dashboard +1. Navigate to `ui/litellm-dashboard` (dependencies were already installed w/ `make bootstrap`) +2. Start dashboard: `npm run dev` ### Verify Docker Image Signatures From 85f9bdd4129588cdf47c746978fc4b658faec747 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 11 Jul 2026 21:38:18 -0700 Subject: [PATCH 311/399] feat(router): add Router(plugins=[...]) routing-plugin pipeline (#32972) * feat(router): add Router(plugins=[...]) routing-plugin pipeline Runs a sequence of user-supplied plugins before the routing decision is made. Each plugin reads/mutates a RoutingContext (messages, candidate models, metadata, signals); the narrowed candidate list is enforced when picking a deployment, raising rather than silently falling back if a plugin narrows to zero candidates. Prototype for the routing-plugin pipeline discussed in #32168. * fix(router): use ruff-modern typing, add raw/structured messages to RoutingContext - Use dict/list/X|None instead of Dict/List/Optional in new code, staying within the ruff strict-rule budget ratchet - Extract the guardrail-translation message normalization ComplexityRouter already had into a shared resolve_structured_messages() helper (litellm_core_utils/prompt_templates/factory.py), reused by ComplexityRouter and the new routing-plugin pipeline instead of duplicating it - RoutingContext now exposes both raw_messages (as received) and structured_messages (normalized across chat completions / Anthropic messages / Responses API), mirroring CustomGuardrail.apply_guardrail's pattern, per review feedback on #32972 - Add direct unit tests for _run_routing_plugins and _filter_by_routing_plugin_candidates (router_code_coverage gate requires every router.py function be called by name somewhere in tests/) * fix(test): rename to test_router_routing_plugins.py router_code_coverage.py's AST scanner only inspects test files whose filename contains the substring "router" -- test_routing_plugins.py doesn't match (routing != router), so it silently skipped this file and flagged _run_routing_plugins/_filter_by_routing_plugin_candidates as untested despite the direct unit tests added for them. * fix(router): fail closed when plugins are configured but the resolved routing path can't run them Router.completion() (and other sync entry points) resolves deployments via the synchronous get_available_deployment(), which never runs async_pre_routing_hook and therefore never runs the routing-plugin pipeline. async_get_available_deployment() itself falls back to that same synchronous method for routing strategies without an async-native selector (e.g. legacy "usage-based-routing" v1). Both paths would let a policy plugin (e.g. a deny-all rule) be silently bypassed. Raise instead of silently proceeding when self.routing_plugins is configured and the sync path is reached, since applying the pipeline to every selector path is a larger change out of scope for this PR. Per review: https://github.com/BerriAI/litellm/pull/32972/changes/BASE..bdfb583c2c6f8df10004fb249e11629d41ce71fa#r3565373303 --- .../prompt_templates/factory.py | 53 +++++ litellm/router.py | 101 ++++++++ .../complexity_router/complexity_router.py | 38 +-- litellm/types/router.py | 31 ++- .../test_router_routing_plugins.py | 220 ++++++++++++++++++ 5 files changed, 407 insertions(+), 36 deletions(-) create mode 100644 tests/test_litellm/router_strategy/test_router_routing_plugins.py diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 8bb0e12905e..f7ff4d6b16f 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -5494,3 +5494,56 @@ def has_tool_with_name(tools: Any, tool_name: str) -> bool: elif tool.get("name") == tool_name: return True return False + + +def resolve_structured_messages( + messages: list[dict[str, Any]] | None, + request_kwargs: dict[str, Any], +) -> list[dict[str, Any]] | None: + """ + Normalize a request's messages to OpenAI-spec chat-completions shape, + regardless of which API surface produced them (chat completions, + Anthropic /v1/messages, Responses API ``input``, etc). + + Returns ``messages`` unchanged if already present. Otherwise dispatches + through the guardrail translation handlers (the same per-surface + conversion logic guardrails use) to convert e.g. Responses API ``input`` + into a message list. Returns ``None`` if no messages could be resolved. + """ + if messages: + return messages + + from litellm.litellm_core_utils.api_route_to_call_types import ( + get_call_types_for_route, + ) + from litellm.llms import load_guardrail_translation_mappings + from litellm.types.utils import CallTypes + + mappings = load_guardrail_translation_mappings() + call_type: CallTypes | None = None + + # 1. Try route-based inference from proxy metadata + route = request_kwargs.get("litellm_metadata", {}).get("user_api_key_request_route") + if route: + call_types_list = get_call_types_for_route(route) + if call_types_list: + for ct in call_types_list: + if ct in mappings: + call_type = ct + break + + # 2. Fallback: try each mapped handler until one produces messages + handlers_to_try: list[Any] = [] + if call_type is not None and call_type in mappings: + handlers_to_try.append(mappings[call_type]()) + else: + handlers_to_try.extend(handler_cls() for handler_cls in mappings.values()) + + for handler in handlers_to_try: + structured = handler.get_structured_messages(request_kwargs) + if structured: + return [ + msg if isinstance(msg, dict) else msg.model_dump() # type: ignore + for msg in structured + ] + return None diff --git a/litellm/router.py b/litellm/router.py index 6e773a06c7f..245a50545e7 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -179,7 +179,9 @@ from litellm.types.router import ( RouterModelGroupAliasItem, RouterRateLimitError, RouterRateLimitErrorBasic, + RoutingContext, RoutingGroup, + RoutingPlugin, RoutingStrategy, SearchToolTypedDict, ) @@ -299,6 +301,7 @@ class Router: enable_pre_call_checks: bool = False, enable_tag_filtering: bool = False, tag_filtering_match_any: bool = True, + plugins: list[RoutingPlugin] | None = None, retry_after: int = 0, # min time to wait before retrying a failed request retry_policy: Optional[Union[RetryPolicy, dict]] = None, # set custom retries for different exceptions model_group_retry_policy: Dict[str, RetryPolicy] = {}, # set custom retry policies based on model group @@ -477,6 +480,7 @@ class Router: self.complexity_routers: Dict[str, "ComplexityRouter"] = {} self.adaptive_routers: Dict[str, "AdaptiveRouter"] = {} self.quality_routers: Dict[str, "QualityRouter"] = {} + self.routing_plugins: list[RoutingPlugin] = list(plugins) if plugins else [] # Initialize model_group_alias early since it's used in set_model_list self.model_group_alias: Dict[str, Union[str, RouterModelGroupAliasItem]] = ( @@ -10321,6 +10325,12 @@ class Router: metadata_variable_name=self._get_metadata_variable_name_from_kwargs(request_kwargs), ) + # narrow to whatever `self.routing_plugins` left in candidate_models + healthy_deployments = self._filter_by_routing_plugin_candidates( + healthy_deployments=healthy_deployments, + request_kwargs=request_kwargs, + ) + ## ORDER FILTERING ## -> if user set 'order' in deployments, return deployments with lowest order (e.g. order=1 > order=2) _target_order = (request_kwargs or {}).pop("_target_order", None) healthy_deployments = litellm.utils._get_order_filtered_deployments( @@ -10596,6 +10606,76 @@ class Router: ) raise e + async def _run_routing_plugins( + self, + model: str, + request_kwargs: dict, + messages: list[dict[str, Any]] | None, + ) -> RoutingContext: + """ + Build a RoutingContext for `model`, run it through `self.routing_plugins` + in order, then stash the narrowed candidate list and accumulated signals + onto `request_kwargs["metadata"]` so `_filter_by_routing_plugin_candidates` + (called later, during healthy-deployment filtering) can consume them. + """ + from litellm.litellm_core_utils.prompt_templates.factory import ( + resolve_structured_messages, + ) + + deployments = self.get_model_list(model_name=model) or [] + candidate_models = [ + d["litellm_params"]["model"] for d in deployments if d.get("litellm_params", {}).get("model") + ] + + metadata_key = self._get_metadata_variable_name_from_kwargs(request_kwargs) + metadata = request_kwargs.setdefault(metadata_key, {}) + + context = RoutingContext( + raw_messages=messages or [], + structured_messages=resolve_structured_messages(messages=messages, request_kwargs=request_kwargs) or [], + candidate_models=candidate_models, + metadata=metadata, + ) + + for plugin in self.routing_plugins: + context = await plugin.run(context) + + metadata["routing_plugin_signals"] = context.signals + if len(context.candidate_models) < len(candidate_models): + metadata["_routing_plugin_candidate_models"] = context.candidate_models + + return context + + def _filter_by_routing_plugin_candidates( + self, + healthy_deployments: Union[list[dict], dict], + request_kwargs: dict, + ) -> Union[list[dict], dict]: + """ + Narrow `healthy_deployments` to whatever `self.routing_plugins` left in + `context.candidate_models`. Raises rather than silently falling back to + the unfiltered pool -- a plugin narrowing to nothing is a policy decision + (e.g. no model this tenant's budget allows), not something to bypass. + """ + if not self.routing_plugins or not isinstance(healthy_deployments, list): + return healthy_deployments + + metadata_key = self._get_metadata_variable_name_from_kwargs(request_kwargs) + candidate_models = (request_kwargs.get(metadata_key) or {}).get("_routing_plugin_candidate_models") + # `is None` (not falsy-check): a plugin narrowing to an empty list must + # still hit the "no deployments left" raise below, not be treated the + # same as "no plugin ever set this key". + if candidate_models is None: + return healthy_deployments + + candidate_set = set(candidate_models) + filtered = [d for d in healthy_deployments if d.get("litellm_params", {}).get("model") in candidate_set] + + if not filtered: + raise ValueError(f"No deployments left after routing-plugin filtering. candidate_models={candidate_models}") + + return filtered + async def async_pre_routing_hook( self, model: str, @@ -10609,6 +10689,15 @@ class Router: Used for the litellm auto-router to modify the request before the routing decision is made. """ + ######################################################### + # Run the routing-plugin pipeline, if any plugins are configured. + # Plugins narrow the candidate deployment pool (consumed later by + # `_filter_by_routing_plugin_candidates`) and may attach signals for + # downstream strategies (auto-router, complexity-router, ...) to read. + ######################################################### + if self.routing_plugins: + await self._run_routing_plugins(model=model, request_kwargs=request_kwargs, messages=messages) + ######################################################### # Check if any auto-router should be used ######################################################### @@ -10671,6 +10760,18 @@ class Router: """ Returns the deployment based on routing strategy """ + if self.routing_plugins: + raise ValueError( + "Router(plugins=[...]) is configured, but this call resolved to the synchronous " + "deployment-selection path, which never runs the routing-plugin pipeline. This " + "happens for sync Router methods (e.g. Router.completion()) and for async calls " + "with a routing_strategy that has no async-native selector (e.g. legacy " + "'usage-based-routing', v1). Silently skipping " + "configured plugins would let a policy plugin (e.g. a deny-all rule) be bypassed. " + "Use an async Router method with a supported routing_strategy (simple-shuffle, " + "usage-based-routing-v2, cost-based-routing, latency-based-routing, least-busy), " + "or remove `plugins` from the Router config." + ) # users need to explicitly call a specific deployment, by setting `specific_deployment = True` as completion()/embedding() kwarg # When this was no explicit we had several issues with fallbacks timing out diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 2138a0112a0..74644f01be8 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -601,43 +601,11 @@ class ComplexityRouter(CustomLogger): Uses the guardrail translation handler dispatch to convert Responses API ``input`` (or other non-chat-completions formats) into OpenAI-spec messages. """ - if messages: - return messages - - from litellm.litellm_core_utils.api_route_to_call_types import ( - get_call_types_for_route, + from litellm.litellm_core_utils.prompt_templates.factory import ( + resolve_structured_messages, ) - from litellm.llms import load_guardrail_translation_mappings - from litellm.types.utils import CallTypes - mappings = load_guardrail_translation_mappings() - call_type: Optional[CallTypes] = None - - # 1. Try route-based inference from proxy metadata - route = request_kwargs.get("litellm_metadata", {}).get("user_api_key_request_route") - if route: - call_types_list = get_call_types_for_route(route) - if call_types_list: - for ct in call_types_list: - if ct in mappings: - call_type = ct - break - - # 2. Fallback: try each mapped handler until one produces messages - handlers_to_try: List[Any] = [] - if call_type is not None and call_type in mappings: - handlers_to_try.append(mappings[call_type]()) - else: - handlers_to_try.extend(handler_cls() for handler_cls in mappings.values()) - - for handler in handlers_to_try: - structured = handler.get_structured_messages(request_kwargs) - if structured: - return [ - msg if isinstance(msg, dict) else msg.model_dump() # type: ignore - for msg in structured - ] - return None + return resolve_structured_messages(messages=messages, request_kwargs=request_kwargs) @staticmethod def _extract_user_message_and_system_prompt( diff --git a/litellm/types/router.py b/litellm/types/router.py index 4bac9358392..3bedd97c20c 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -9,7 +9,7 @@ from typing import Any, Dict, List, Literal, Optional, Tuple, Union, get_type_hi import httpx from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from typing_extensions import Required, TypedDict +from typing_extensions import Protocol, Required, TypedDict from litellm._uuid import uuid @@ -829,6 +829,35 @@ class PreRoutingHookResponse(BaseModel): messages: Optional[List[Dict[str, Any]]] +class RoutingContext(BaseModel): + """ + Passed through a Router's `plugins` pipeline before the routing decision is made. + + Each plugin reads and mutates this object; the next plugin sees the previous + plugin's changes. `candidate_models` narrows as the pipeline runs -- Router + only selects a deployment whose `litellm_params.model` survives the pipeline. + + `raw_messages` and `structured_messages` mirror the pattern + `CustomGuardrail.apply_guardrail` uses: the message shape differs by API + surface (chat completions, Anthropic /v1/messages, Responses API `input`, + ...), so plugins that need a stable, provider-agnostic shape should read + `structured_messages` (normalized to OpenAI chat-completions format); + plugins that need the exact original payload can read `raw_messages`. + """ + + raw_messages: list[dict[str, Any]] + structured_messages: list[dict[str, Any]] + candidate_models: list[str] + metadata: dict[str, Any] = Field(default_factory=dict) + signals: dict[str, Any] = Field(default_factory=dict) + + +class RoutingPlugin(Protocol): + """Interface a custom routing plugin must implement to run in `Router(plugins=[...])`.""" + + async def run(self, context: RoutingContext) -> RoutingContext: ... + + class RequestType(str, enum.Enum): """Fixed v0 taxonomy. User-extensible types come in v1.""" diff --git a/tests/test_litellm/router_strategy/test_router_routing_plugins.py b/tests/test_litellm/router_strategy/test_router_routing_plugins.py new file mode 100644 index 00000000000..e9c12d009e2 --- /dev/null +++ b/tests/test_litellm/router_strategy/test_router_routing_plugins.py @@ -0,0 +1,220 @@ +""" +Tests for Router(plugins=[...]) -- a pipeline of routing plugins that run +before the routing decision is made, narrowing the candidate deployment pool. + +Discussion: https://github.com/BerriAI/litellm/discussions/32168 +""" + +import pytest + +from litellm import Router +from litellm.types.router import RoutingContext + + +class LanguageDetector: + async def run(self, context: RoutingContext) -> RoutingContext: + context.signals["language-detector"] = {"lang": "en"} + return context + + +class DomainClassifier: + async def run(self, context: RoutingContext) -> RoutingContext: + context.signals["domain-classifier"] = {"domain": "coding", "confidence": 0.93} + return context + + +class TenantPolicy: + ALLOWED_PROVIDERS = {"acme-corp": {"openai", "anthropic"}} + + async def run(self, context: RoutingContext) -> RoutingContext: + tenant = context.metadata.get("tenant", "default") + allowed = self.ALLOWED_PROVIDERS.get(tenant, {"openai", "anthropic", "self-hosted"}) + context.candidate_models = [m for m in context.candidate_models if m.split("/")[0] in allowed] + context.signals["tenant-policy"] = {"tenant": tenant, "allowed_providers": sorted(allowed)} + return context + + +class BudgetPolicy: + COST_CAP_PER_TOKEN = 0.000005 + COST_BY_MODEL = { + "openai/gpt-4o-mini": 0.00000015, + "anthropic/claude-haiku-4-5": 0.000001, + "openai/gpt-5.1": 0.00003, + } + + async def run(self, context: RoutingContext) -> RoutingContext: + context.candidate_models = [ + m for m in context.candidate_models if self.COST_BY_MODEL.get(m, 0) <= self.COST_CAP_PER_TOKEN + ] + context.signals["budget-policy"] = {"daily_limit": 100} + return context + + +class BlockEverything: + async def run(self, context: RoutingContext) -> RoutingContext: + context.candidate_models = [] + return context + + +def _smart_router_model_list(): + return [ + { + "model_name": "smart-router", + "litellm_params": {"model": "openai/gpt-4o-mini", "mock_response": "cheap openai"}, + "model_info": {"tags": ["openai"]}, + }, + { + "model_name": "smart-router", + "litellm_params": {"model": "anthropic/claude-haiku-4-5", "mock_response": "anthropic"}, + "model_info": {"tags": ["anthropic"]}, + }, + { + "model_name": "smart-router", + "litellm_params": {"model": "openai/gpt-5.1", "mock_response": "expensive openai"}, + "model_info": {"tags": ["openai"]}, + }, + { + "model_name": "smart-router", + "litellm_params": {"model": "ollama/llama-3-70b", "mock_response": "self hosted"}, + "model_info": {"tags": ["self-hosted"]}, + }, + ] + + +@pytest.mark.asyncio +async def test_routing_plugin_pipeline_matches_jeann2013_e2e_scenario(): + """ + https://github.com/BerriAI/litellm/discussions/32168#discussioncomment-17608820 + + language plugin -> domain classifier -> tenant policy (openai+anthropic only) + -> budget policy (drops over-cap models) -> Router picks the best remaining + candidate. Must never land on the self-hosted or over-budget deployment. + """ + router = Router( + model_list=_smart_router_model_list(), + plugins=[LanguageDetector(), DomainClassifier(), TenantPolicy(), BudgetPolicy()], + ) + + response = await router.acompletion( + model="smart-router", + messages=[{"role": "user", "content": "Write a function to reverse a linked list."}], + metadata={"tenant": "acme-corp"}, + ) + + # response.model is the bare model name (litellm strips the provider/ prefix + # on the response), so compare against bare names rather than litellm_params.model + routed_model = response.model + + assert routed_model in {"gpt-4o-mini", "claude-haiku-4-5"} + assert routed_model not in {"llama-3-70b", "gpt-5.1"} + + +@pytest.mark.asyncio +async def test_routing_plugin_narrowing_to_zero_candidates_raises(): + """A plugin narrowing to nothing is a policy decision -- must raise, not silently + fall back to the unfiltered pool (that would defeat the policy it enforces).""" + router = Router( + model_list=_smart_router_model_list(), + plugins=[BlockEverything()], + ) + + with pytest.raises(ValueError, match="No deployments left after routing-plugin filtering"): + await router.acompletion( + model="smart-router", + messages=[{"role": "user", "content": "hi"}], + ) + + +def test_sync_get_available_deployment_rejects_configured_plugins(): + """ + Router.completion() (and any other sync entry point) resolves deployments via + the synchronous get_available_deployment(), which never runs the routing-plugin + pipeline. Silently allowing that would let a deny-all policy plugin be bypassed + just by calling the sync API -- must fail closed instead. + """ + router = Router(model_list=_smart_router_model_list(), plugins=[TenantPolicy()]) + + with pytest.raises(ValueError, match="routing-plugin pipeline"): + router.get_available_deployment(model="smart-router", messages=[{"role": "user", "content": "hi"}]) + + +def test_sync_router_completion_rejects_configured_plugins(): + """End-to-end: Router.completion() (the sync API) must not silently skip plugins either.""" + router = Router(model_list=_smart_router_model_list(), plugins=[TenantPolicy()]) + + with pytest.raises(ValueError, match="routing-plugin pipeline"): + router.completion(model="smart-router", messages=[{"role": "user", "content": "hi"}]) + + +@pytest.mark.asyncio +async def test_async_completion_with_unsupported_strategy_rejects_configured_plugins(): + """ + async_get_available_deployment() itself delegates to the synchronous selector + for routing strategies outside {simple-shuffle, usage-based-routing-v2, + cost-based-routing, latency-based-routing, least-busy} -- e.g. "usage-based-routing" + (v1, not v2) -- which would silently bypass the plugin pipeline on the async path too. + """ + router = Router( + model_list=_smart_router_model_list(), + plugins=[TenantPolicy()], + routing_strategy="usage-based-routing", + ) + + with pytest.raises(ValueError, match="routing-plugin pipeline"): + await router.acompletion(model="smart-router", messages=[{"role": "user", "content": "hi"}]) + + +@pytest.mark.asyncio +async def test_router_without_plugins_is_unaffected(): + """Regression guard: a Router with no `plugins` configured behaves exactly as before.""" + router = Router( + model_list=[ + { + "model_name": "smart-router", + "litellm_params": {"model": "openai/gpt-4o-mini", "mock_response": "hi"}, + }, + ], + ) + response = await router.acompletion( + model="smart-router", + messages=[{"role": "user", "content": "hi"}], + ) + assert response.choices[0].message.content == "hi" + + +@pytest.mark.asyncio +async def test_run_routing_plugins_narrows_candidates_and_records_signals(): + """Unit-level check of _run_routing_plugins in isolation, independent of acompletion.""" + router = Router( + model_list=_smart_router_model_list(), + plugins=[LanguageDetector(), DomainClassifier(), TenantPolicy(), BudgetPolicy()], + ) + request_kwargs = {"metadata": {"tenant": "acme-corp"}} + + context = await router._run_routing_plugins( + model="smart-router", + request_kwargs=request_kwargs, + messages=[{"role": "user", "content": "hi"}], + ) + + assert context.candidate_models == ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5"] + assert context.signals["domain-classifier"]["domain"] == "coding" + assert request_kwargs["metadata"]["_routing_plugin_candidate_models"] == context.candidate_models + + +def test_filter_by_routing_plugin_candidates_narrows_and_raises_when_empty(): + """Unit-level check of _filter_by_routing_plugin_candidates in isolation.""" + router = Router(model_list=_smart_router_model_list(), plugins=[TenantPolicy()]) + healthy_deployments = router.model_list + + narrowed = router._filter_by_routing_plugin_candidates( + healthy_deployments=healthy_deployments, + request_kwargs={"metadata": {"_routing_plugin_candidate_models": ["openai/gpt-4o-mini"]}}, + ) + assert [d["litellm_params"]["model"] for d in narrowed] == ["openai/gpt-4o-mini"] + + with pytest.raises(ValueError, match="No deployments left after routing-plugin filtering"): + router._filter_by_routing_plugin_candidates( + healthy_deployments=healthy_deployments, + request_kwargs={"metadata": {"_routing_plugin_candidate_models": ["nonexistent/model"]}}, + ) From 26ab730bfaac36b6d96af68d5fe5e7eb867af2ca Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 11 Jul 2026 21:56:33 -0700 Subject: [PATCH 312/399] feat(router): soft-floor adaptive mode for complexity router (#32947) * feat(router): soft-floor adaptive mode for complexity router Let complexity_router_config.adaptive=true Thompson-sample across the union of tier pools with a tier-distance penalty, and wire the existing adaptive post-call bandit so mis-tiered requests can still recover. Co-authored-by: Cursor * fix(router): reattach adaptive hooks for hybrid complexity Finalize was wiping every AdaptiveRouterPostCallHook and only re-registering standalone auto_router/adaptive_router deployments, so complexity adaptive=true never received bandit updates. Co-authored-by: Cursor * chore(router): drop unnecessary hybrid docstrings Co-authored-by: Cursor * fix(router): attribute adaptive feedback Credit user reactions to the model that produced the previous response while keeping current-response signals on the serving model Co-authored-by: Cursor * fix(router): tune hybrid cold defaults Use the cost-weighted policy that beat equal-pool complexity in the full bakeoff, and make the committed harness compare identical tier pools Co-authored-by: Cursor * fix(router): preserve hybrid cold quality floor Sample only unobserved models in the classified tier until feedback exists, then apply adaptive scoring without mis-penalizing models shared across tiers Co-authored-by: Cursor * fix(router): bound feedback context cache Cap retained session feedback so unique session IDs cannot exhaust router memory Co-authored-by: Cursor * fix(router): preserve exhaustion signals Include tool-result exhaustion in adaptive feedback and clear strict lint regressions blocking CI Co-authored-by: Cursor * refactor(router): remove stale owner cache Remove obsolete attribution state, tighten the embedded router type, and keep the test diff focused on adaptive behavior Co-authored-by: Cursor * refactor(router): centralize hook cleanup Use the callback manager to discover and remove adaptive hooks across every registered callback list Co-authored-by: Cursor --------- Co-authored-by: Cursor --- litellm/router.py | 29 +- .../router_strategy/adaptive_router/README.md | 15 +- .../adaptive_router/adaptive_router.py | 290 +++++++++++------- .../router_strategy/adaptive_router/hooks.py | 8 - .../adaptive_router/signals.py | 148 ++++++--- .../complexity_router/complexity_router.py | 271 +++++++++++++--- .../complexity_router/config.py | 87 ++++-- ruff-strict-budget.json | 8 +- .../adaptive_router/test_adaptive_router.py | 216 +++++++------ .../test_e2e_adaptive_router.py | 17 - .../adaptive_router/test_hooks.py | 37 ++- .../adaptive_router/test_state_endpoint.py | 25 +- .../router_strategy/test_complexity_router.py | 288 +++++++++++++++++ .../add_model/ComplexityRouterConfig.tsx | 2 +- .../build_complexity_router_config.ts | 3 +- 15 files changed, 1035 insertions(+), 409 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 245a50545e7..6539d3c0c43 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -7556,7 +7556,11 @@ class Router: if default_model is None and complexity_router_config: tiers = complexity_router_config.get("tiers", {}) # Use MEDIUM tier as fallback default - default_model = tiers.get("MEDIUM") or tiers.get("SIMPLE") + medium = tiers.get("MEDIUM") or tiers.get("SIMPLE") + if isinstance(medium, list): + default_model = medium[0] if medium else None + else: + default_model = medium if default_model is None: raise ValueError( @@ -7593,15 +7597,6 @@ class Router: AdaptiveRouterPostCallHook, ) - for _cb_list in ( - litellm.callbacks, - litellm.success_callback, - litellm.failure_callback, - litellm._async_success_callback, - litellm._async_failure_callback, - ): - litellm.logging_callback_manager.remove_callbacks_by_type(_cb_list, AdaptiveRouterPostCallHook) - for entry in self.model_list or []: lp = entry.get("litellm_params") if isinstance(entry, dict) else entry.litellm_params lp_model = (lp.get("model") if isinstance(lp, dict) else lp.model) if lp else None @@ -7619,6 +7614,20 @@ class Router: ) self.init_adaptive_router_deployment(deployment=deployment) + for model_name, complexity_router in self.complexity_routers.items(): + if not complexity_router.config.adaptive or model_name in self.adaptive_routers: + continue + adaptive_router = complexity_router._ensure_adaptive_router() + if adaptive_router is not None: + self.adaptive_routers[model_name] = adaptive_router + + for callback in litellm.logging_callback_manager.get_custom_loggers_for_type(AdaptiveRouterPostCallHook): + litellm.logging_callback_manager.remove_callback_from_all_lists(callback) + for adaptive_router in self.adaptive_routers.values(): + litellm.logging_callback_manager.add_litellm_callback( + AdaptiveRouterPostCallHook(adaptive_router=adaptive_router) + ) + def init_adaptive_router_deployment(self, deployment: Deployment) -> None: """ Build an AdaptiveRouter instance for this deployment and register its diff --git a/litellm/router_strategy/adaptive_router/README.md b/litellm/router_strategy/adaptive_router/README.md index 7f5d7aa21d0..09420a8dd9d 100644 --- a/litellm/router_strategy/adaptive_router/README.md +++ b/litellm/router_strategy/adaptive_router/README.md @@ -56,11 +56,10 @@ Callers may pass header `x-litellm-min-quality-tier: 3` (or metadata key - **Per-request decision.** Sample once per eligible model, score with `quality_weight·sample + cost_weight·normalized_cost`, pick the argmax. Routing is stateless per-turn — no sticky lookup. Each call resamples. -- **Owner-cache attribution.** Post-call, the conversation's first picked - model claims an "owner slot" for `OWNER_CACHE_TTL_SECONDS` (24h). Later - turns of the same conversation only fire bandit/state updates if the - same model handled them — mismatches are dropped (no attribution) and - counted in `skipped_updates_total`. Conversation identity is the +- **Previous-response attribution.** Post-call, feedback from the current user + message is attributed to the model that produced the previous response, while + response signals are attributed to the current model. Contexts expire after + 24 hours and the in-memory cache is capped at 1,024 sessions. Conversation identity is the client-supplied `litellm_session_id` if present, otherwise a sha256 over caller identity (api key hash, team, user, end-user) + the first message. - **Per-turn updates.** `satisfaction → +α`. `misalignment, stagnation, @@ -76,12 +75,6 @@ Callers may pass header `x-litellm-min-quality-tier: 3` (or metadata key model can still be picked. - **Hard sample cap at 200.** Once `α + β > 200`, deltas are silently dropped. No rescaling — drift is a v1 concern. -- **24h owner-cache TTL.** No explicit eviction below TTL. The in-memory map - can grow if traffic patterns produce many one-shot sessions. -- **Owner-recovery skew.** If model A "owns" a conversation but is then - dethroned in the bandit, later turns served by model B are dropped — so - bandit updates for that conversation flatline until A's TTL expires. - Tracked via `skipped_updates_total`. - **Signals are regex + tool-call only.** No LLM-judge, no embedding similarity, no exemplar storage. Signals are best-effort and biased toward English. - **One AdaptiveRouter per `Router`.** Multiple `adaptive_router/*` deployments diff --git a/litellm/router_strategy/adaptive_router/adaptive_router.py b/litellm/router_strategy/adaptive_router/adaptive_router.py index 69d6a019e68..ec84eb1decf 100644 --- a/litellm/router_strategy/adaptive_router/adaptive_router.py +++ b/litellm/router_strategy/adaptive_router/adaptive_router.py @@ -3,25 +3,21 @@ Main adaptive router strategy. See README.md for design overview. One AdaptiveRouter instance per router_name. Holds in-memory caches: - _cells: Beta(alpha, beta) bandit posteriors per (request_type, model) -- _owner_cache: session_key -> (owner_model, expires_at) — the first model - picked for a conversation owns its bandit-update slot - _session_states: (session_key, model) -> SessionState for incremental signal updates Owns the AdaptiveRouterUpdateQueue used by the proxy's flusher to persist state and session snapshots back to Postgres. -Routing is stateless per-turn (Thompson sample fresh on every call). The -owner cache is consulted only at post-call time to decide whether a turn's -signals should fire a bandit update — turns served by a different model than -the conversation's owner are skipped to avoid cross-model misattribution. +Routing is stateless per-turn (Thompson sample fresh on every call). """ from __future__ import annotations import asyncio import time -from dataclasses import asdict -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from collections import OrderedDict +from dataclasses import asdict, dataclass +from typing import Any, Union, cast from litellm._logging import verbose_router_logger from litellm.litellm_core_utils.prompt_templates.common_utils import ( @@ -38,13 +34,18 @@ from litellm.router_strategy.adaptive_router.config import ( ADAPTIVE_ROUTER_CHOSEN_MODEL_KEY, MIN_QUALITY_TIER_HEADER, MIN_QUALITY_TIER_METADATA_KEY, + MIN_TURNS_FOR_CLEAN_CREDIT, OWNER_CACHE_TTL_SECONDS, ) from litellm.router_strategy.adaptive_router.signals import ( SessionState, SignalDelta, Turn, - apply_turn, + advance_session_state, + apply_signal_delta, + detect_response_signals, + detect_user_feedback, + merge_signal_deltas, ) from litellm.router_strategy.adaptive_router.update_queue import ( AdaptiveRouterUpdateQueue, @@ -53,8 +54,7 @@ from litellm.router_strategy.adaptive_router.update_queue import ( # Sweep session-state cache when it exceeds this many live entries. Expired # entries are dropped in bulk; amortizes to O(1) per insert. _SESSION_STATE_SWEEP_THRESHOLD: int = 1024 -# Same pattern for the owner cache. -_OWNER_CACHE_SWEEP_THRESHOLD: int = 1024 +_FEEDBACK_CONTEXT_MAX_ENTRIES: int = 1024 from litellm.repositories.table_repositories import AdaptiveRouterStateRepository from litellm.types.llms.openai import AllMessageValues from litellm.types.router import ( @@ -70,6 +70,17 @@ def _default_prefs() -> AdaptiveRouterPreferences: return AdaptiveRouterPreferences(quality_tier=2, strengths=[]) +@dataclass(frozen=True, slots=True) +class _FeedbackContext: + model_name: str + request_type: RequestType + user_content: str | None + assistant_content: str | None + turn_count: int + clean_credit_awarded: bool + expires_at: float + + class AdaptiveRouter: """One instance per router_name. Holds in-memory caches + the update queue.""" @@ -77,8 +88,8 @@ class AdaptiveRouter: self, router_name: str, config: AdaptiveRouterConfig, - model_to_prefs: Dict[str, AdaptiveRouterPreferences], - model_to_cost: Dict[str, float], + model_to_prefs: dict[str, AdaptiveRouterPreferences], + model_to_cost: dict[str, float], ) -> None: self.router_name = router_name self.config = config @@ -86,13 +97,14 @@ class AdaptiveRouter: self.model_to_cost = model_to_cost self.queue = AdaptiveRouterUpdateQueue() - self._cells: Dict[Tuple[RequestType, str], BanditCell] = {} - self._owner_cache: Dict[str, Tuple[str, float]] = {} - self._session_states: Dict[Tuple[str, str], SessionState] = {} - # Parallel expiry map for _session_states, same TTL as _owner_cache. - # Evicted opportunistically in `get_or_create_session_state`. - self._session_states_expiry: Dict[Tuple[str, str], float] = {} - self._skipped_updates_total: int = 0 + self._cells: dict[tuple[RequestType, str], BanditCell] = {} + self._session_states: dict[tuple[str, str], SessionState] = {} + self._feedback_contexts: OrderedDict[str, _FeedbackContext] = OrderedDict() + self._session_states_expiry: dict[tuple[str, str], float] = {} + self._feedback_attributed_total: int = 0 + self._feedback_without_context_total: int = 0 + self._cross_model_feedback_total: int = 0 + self._response_signal_updates_total: int = 0 # Set to True once the proxy flusher has loaded persisted priors from # Postgres. Checked to support lazy-load on hot-reloaded routers. self._state_loaded: bool = False @@ -145,11 +157,11 @@ class AdaptiveRouter: async def async_pre_routing_hook( self, model: str, - request_kwargs: Dict[str, Any], - messages: Optional[List[Dict[str, Any]]] = None, - input: Optional[Union[str, List]] = None, - specific_deployment: Optional[bool] = False, - ) -> Optional[PreRoutingHookResponse]: + request_kwargs: dict[str, Any], + messages: list[dict[str, Any]] | None = None, + input: Union[str, list] | None = None, + specific_deployment: bool | None = False, + ) -> PreRoutingHookResponse | None: """ Plugin entry point invoked by `Router.async_pre_routing_hook` when the inbound `model` matches this adaptive router's `router_name`. @@ -159,11 +171,9 @@ class AdaptiveRouter: post-call hook can surface it as a response header. Routing is stateless per-turn: every call Thompson-samples fresh, - regardless of any prior pick for the same session. Cross-turn - attribution is enforced post-call via the owner cache (see - `claim_or_check_owner`). + regardless of any prior pick for the same session. """ - user_text = get_last_user_message(cast(List[AllMessageValues], messages or [])) or "" + user_text = get_last_user_message(cast(list[AllMessageValues], messages or [])) or "" request_type = classify_prompt(user_text) min_quality_tier = self._extract_min_quality_tier(request_kwargs) @@ -190,7 +200,7 @@ class AdaptiveRouter: async def pick_model( self, request_type: RequestType, - min_quality_tier: Optional[int] = None, + min_quality_tier: int | None = None, ) -> str: """Thompson-sample across eligible models. Stateless per-turn.""" eligible = self._eligible_models(min_quality_tier) @@ -206,44 +216,7 @@ class AdaptiveRouter: cost_weight=self.config.weights.cost, ) - def claim_or_check_owner(self, session_key: str, current_model: str) -> bool: - """Resolve attribution for a turn under stateless routing. - - Returns True iff this turn should fire a bandit/state update. The - first call for a `session_key` claims ownership for `current_model` - and returns True. Subsequent calls return True only if the owner is - still live AND matches `current_model`. Mismatches (a different - model handled this turn) and expired owners both increment - `_skipped_updates_total` and return False — no attribution. - """ - now = time.time() - existing = self._owner_cache.get(session_key) - if existing is not None and existing[1] > now: - owner_model, _ = existing - if owner_model == current_model: - return True - self._skipped_updates_total += 1 - return False - - # Opportunistic bulk sweep — sessions that never come back would - # otherwise pile up here forever. Same threshold pattern as the - # session-state cache. - if len(self._owner_cache) >= _OWNER_CACHE_SWEEP_THRESHOLD: - self._evict_expired_owner_cache(now) - - # No live owner -> claim for current_model. - self._owner_cache[session_key] = ( - current_model, - now + OWNER_CACHE_TTL_SECONDS, - ) - return True - - def _evict_expired_owner_cache(self, now: float) -> None: - expired = [k for k, (_, exp) in self._owner_cache.items() if exp <= now] - for k in expired: - self._owner_cache.pop(k, None) - - async def get_state_snapshot(self) -> Dict[str, Any]: + async def get_state_snapshot(self) -> dict[str, Any]: """In-memory snapshot for the introspection endpoint. Cheap; no DB hit.""" cells = [] for (rt, model), cell in sorted(self._cells.items(), key=lambda kv: (kv[0][0].value, kv[0][1])): @@ -264,7 +237,7 @@ class AdaptiveRouter: ) queue = await self.queue.queue_size() now = time.time() - owner_cache_live = sum(1 for _, exp in self._owner_cache.values() if exp > now) + feedback_contexts_live = sum(1 for context in self._feedback_contexts.values() if context.expires_at > now) return { "router_name": self.router_name, "available_models": list(self.config.available_models), @@ -274,15 +247,18 @@ class AdaptiveRouter: }, "model_costs": dict(self.model_to_cost), "cells": cells, - "owner_cache_live": owner_cache_live, - "skipped_updates_total": self._skipped_updates_total, + "feedback_contexts_live": feedback_contexts_live, + "feedback_attributed_total": self._feedback_attributed_total, + "feedback_without_context_total": self._feedback_without_context_total, + "cross_model_feedback_total": self._cross_model_feedback_total, + "response_signal_updates_total": self._response_signal_updates_total, "queue": queue, } @staticmethod def _extract_min_quality_tier( - request_kwargs: Dict[str, Any], - ) -> Optional[int]: + request_kwargs: dict[str, Any], + ) -> int | None: """Pull `min_quality_tier` from request headers or metadata. Precedence: headers (`x-litellm-min-quality-tier`) over metadata @@ -310,7 +286,7 @@ class AdaptiveRouter: return None return None - def _eligible_models(self, min_quality_tier: Optional[int]) -> List[str]: + def _eligible_models(self, min_quality_tier: int | None) -> list[str]: if min_quality_tier is None: return list(self.config.available_models) return [ @@ -363,17 +339,131 @@ class AdaptiveRouter: request_type: RequestType, turn: Turn, ) -> SignalDelta: - """Apply one turn, push session snapshot + bandit deltas to the queue.""" - state = self.get_or_create_session_state(session_id, model_name, request_type) - delta = apply_turn(state, turn) - verbose_router_logger.debug("AdaptiveRouter[%s]: record_turn delta=%s", self.router_name, delta) + """Attribute feedback to the previous response and response signals to the current model.""" + async with self._lock: + now = time.time() + while self._feedback_contexts: + oldest_context = next(iter(self._feedback_contexts.values())) + if oldest_context.expires_at > now: + break + self._feedback_contexts.popitem(last=False) + previous = self._feedback_contexts.pop(session_id, None) - # Strip the raw conversation content before persisting. The - # last_user/assistant_content and tool_call_history fields are only - # needed in-memory for the next turn's incremental signal detection; - # writing user prompts and tool payloads to the DB would store PII - # for every adaptive-router conversation. Counts + bookkeeping is - # all the persisted row needs. + effective_request_type = ( + previous.request_type if previous is not None and request_type == RequestType.GENERAL else request_type + ) + current_state = self.get_or_create_session_state( + session_id, + model_name, + effective_request_type, + ) + feedback_delta = detect_user_feedback( + previous.user_content if previous else None, + turn.user_content, + turn.tool_results, + allow_satisfaction=( + previous is not None + and not previous.clean_credit_awarded + and previous.turn_count + 1 >= MIN_TURNS_FOR_CLEAN_CREDIT + ), + ) + previous_assistant = previous.assistant_content if previous else None + response_delta = detect_response_signals( + previous_assistant, + turn.assistant_content, + current_state.tool_call_history, + turn.tool_calls, + turn.tool_results, + turn.response_status, + ) + states_to_persist: dict[str, SessionState] = {model_name: current_state} + bandit_deltas: dict[tuple[RequestType, str], SignalDelta] = {} + + if previous is not None: + feedback_state = self.get_or_create_session_state( + session_id, + previous.model_name, + previous.request_type, + ) + apply_signal_delta(feedback_state, feedback_delta) + if feedback_delta.satisfaction: + feedback_state.clean_credit_awarded = True + states_to_persist[previous.model_name] = feedback_state + if feedback_delta.any_fired(): + self._feedback_attributed_total += 1 + if previous.model_name != model_name: + self._cross_model_feedback_total += 1 + bandit_deltas[(previous.request_type, previous.model_name)] = feedback_delta + else: + if feedback_delta.any_fired(): + self._feedback_without_context_total += 1 + initial_failure = SignalDelta(failure=feedback_delta.failure) + apply_signal_delta(current_state, initial_failure) + bandit_deltas[(effective_request_type, model_name)] = initial_failure + + apply_signal_delta(current_state, response_delta) + if self._compute_bandit_delta(response_delta) != (0.0, 0.0): + self._response_signal_updates_total += 1 + current_key = (effective_request_type, model_name) + bandit_deltas[current_key] = merge_signal_deltas( + bandit_deltas.get(current_key, SignalDelta()), + response_delta, + ) + advance_session_state(current_state, turn) + + next_turn_count = (previous.turn_count if previous else 0) + 1 + clean_credit_awarded = bool((previous and previous.clean_credit_awarded) or feedback_delta.satisfaction) + if len(self._feedback_contexts) >= _FEEDBACK_CONTEXT_MAX_ENTRIES: + self._feedback_contexts.popitem(last=False) + self._feedback_contexts[session_id] = _FeedbackContext( + model_name=model_name, + request_type=effective_request_type, + user_content=turn.user_content, + assistant_content=turn.assistant_content, + turn_count=next_turn_count, + clean_credit_awarded=clean_credit_awarded, + expires_at=now + OWNER_CACHE_TTL_SECONDS, + ) + + for state_model, state in states_to_persist.items(): + await self.queue.add_session_state( + session_id, + self.router_name, + state_model, + self._persistable_session_snapshot(state), + ) + + combined_delta = SignalDelta() + for (attribution_type, target_model), delta in bandit_deltas.items(): + combined_delta = merge_signal_deltas(combined_delta, delta) + d_alpha, d_beta = self._compute_bandit_delta(delta) + if d_alpha == 0 and d_beta == 0: + continue + cell_key = (attribution_type, target_model) + self._cells[cell_key] = apply_delta( + self._cells[cell_key], + d_alpha, + d_beta, + ) + await self.queue.add_state_delta( + self.router_name, + attribution_type.value, + target_model, + d_alpha, + d_beta, + ) + + verbose_router_logger.debug( + "AdaptiveRouter[%s]: feedback_target=%s current_model=%s delta=%s", + self.router_name, + previous.model_name if previous else None, + model_name, + combined_delta, + ) + return combined_delta + + @staticmethod + def _persistable_session_snapshot(state: SessionState) -> dict[str, Any]: snapshot = asdict(state) for sensitive in ( "last_user_content", @@ -382,38 +472,10 @@ class AdaptiveRouter: "pending_tool_calls", ): snapshot.pop(sensitive, None) - await self.queue.add_session_state(session_id, self.router_name, model_name, snapshot) - - d_alpha, d_beta = self._compute_bandit_delta(delta) - verbose_router_logger.debug( - "AdaptiveRouter[%s]: bandit delta alpha=%.2f beta=%.2f", - self.router_name, - d_alpha, - d_beta, - ) - if d_alpha != 0 or d_beta != 0: - # For non-GENERAL turns, attribute to the current-turn classification - # so genuine mid-session topic shifts (e.g. code → math) update the - # correct cell. For GENERAL turns ("thanks!", "ok", "sounds good"), fall - # back to the session's original type so closing pleasantries don't - # misattribute the reward. - attribution_type = ( - request_type if request_type != RequestType.GENERAL else RequestType(state.classified_type) - ) - cell_key = (attribution_type, model_name) - self._cells[cell_key] = apply_delta(self._cells[cell_key], d_alpha, d_beta) - await self.queue.add_state_delta( - self.router_name, - attribution_type.value, - model_name, - d_alpha, - d_beta, - ) - - return delta + return snapshot @staticmethod - def _compute_bandit_delta(delta: SignalDelta) -> Tuple[float, float]: + def _compute_bandit_delta(delta: SignalDelta) -> tuple[float, float]: """ Translate per-turn signal deltas into bandit-cell deltas. diff --git a/litellm/router_strategy/adaptive_router/hooks.py b/litellm/router_strategy/adaptive_router/hooks.py index c3e3f8ca74a..89ae28be227 100644 --- a/litellm/router_strategy/adaptive_router/hooks.py +++ b/litellm/router_strategy/adaptive_router/hooks.py @@ -214,10 +214,6 @@ class AdaptiveRouterPostCallHook(CustomLogger): ) -> None: try: messages = kwargs.get("messages") or [] - if len(messages) < SIGNAL_GATE_MIN_MESSAGES: - # Too few turns for any signal to be meaningful — skip. - return - session_key = _resolve_session_key(kwargs) if not session_key: return @@ -233,10 +229,6 @@ class AdaptiveRouterPostCallHook(CustomLogger): if not current_model: return - if not self.adaptive_router.claim_or_check_owner(session_key, current_model): - # A different model owns this conversation — skip attribution. - return - user_text = _last_user_content(messages) assistant_text, tool_calls = _assistant_content_and_tool_calls(response_obj) tool_results = _recent_tool_results(messages) diff --git a/litellm/router_strategy/adaptive_router/signals.py b/litellm/router_strategy/adaptive_router/signals.py index 2fd1d24fbbe..74fa8936098 100644 --- a/litellm/router_strategy/adaptive_router/signals.py +++ b/litellm/router_strategy/adaptive_router/signals.py @@ -14,7 +14,7 @@ from __future__ import annotations import re from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional, Set +from typing import Any from litellm.router_strategy.adaptive_router.config import ( LOOP_REPEAT_THRESHOLD, @@ -74,26 +74,26 @@ class SessionState: loop_count: int = 0 exhaustion_count: int = 0 - last_user_content: Optional[str] = None - last_assistant_content: Optional[str] = None - tool_call_history: List[str] = field(default_factory=list) - pending_tool_calls: Dict[str, str] = field(default_factory=dict) + last_user_content: str | None = None + last_assistant_content: str | None = None + tool_call_history: list[str] = field(default_factory=list) + pending_tool_calls: dict[str, str] = field(default_factory=dict) turn_count: int = 0 last_processed_turn: int = -1 clean_credit_awarded: bool = False - terminal_status: Optional[int] = None + terminal_status: int | None = None @dataclass class Turn: """One turn of input. Caller assembles this from the request/response.""" - user_content: Optional[str] = None - assistant_content: Optional[str] = None - tool_calls: List[Dict[str, Any]] = field(default_factory=list) - tool_results: List[Dict[str, Any]] = field(default_factory=list) - response_status: Optional[int] = None + user_content: str | None = None + assistant_content: str | None = None + tool_calls: list[dict[str, Any]] = field(default_factory=list) + tool_results: list[dict[str, Any]] = field(default_factory=list) + response_status: int | None = None # ---- Detection helpers ---------------------------------------------------- @@ -101,13 +101,13 @@ class Turn: _TOKEN_RE = re.compile(r"[A-Za-z0-9]+") -def _tokens(text: Optional[str]) -> Set[str]: +def _tokens(text: str | None) -> set[str]: if not text: return set() return {t.lower() for t in _TOKEN_RE.findall(text)} -def _jaccard(a: Set[str], b: Set[str]) -> float: +def _jaccard(a: set[str], b: set[str]) -> float: union = a | b if not union: return 0.0 @@ -130,7 +130,7 @@ _SATISFACTION_PATTERNS = [ ] -def _detect_misalignment(prev_user: Optional[str], curr_user: Optional[str]) -> bool: +def _detect_misalignment(prev_user: str | None, curr_user: str | None) -> bool: """Fires when consecutive user messages share *some* topic (jaccard > 0) but are sufficiently different (jaccard < threshold) — i.e. user is rephrasing, not changing topic, not repeating.""" @@ -140,7 +140,7 @@ def _detect_misalignment(prev_user: Optional[str], curr_user: Optional[str]) -> return 0.0 < j < MISALIGNMENT_JACCARD_THRESHOLD -def _detect_stagnation(prev_asst: Optional[str], curr_asst: Optional[str]) -> bool: +def _detect_stagnation(prev_asst: str | None, curr_asst: str | None) -> bool: """Fires when consecutive assistant messages are near-duplicates.""" if not prev_asst or not curr_asst: return False @@ -148,19 +148,19 @@ def _detect_stagnation(prev_asst: Optional[str], curr_asst: Optional[str]) -> bo return j >= STAGNATION_JACCARD_NEAR_DUP -def _detect_disengagement(curr_user: Optional[str]) -> bool: +def _detect_disengagement(curr_user: str | None) -> bool: if not curr_user: return False return any(p.search(curr_user) for p in _DISENGAGEMENT_PATTERNS) -def _detect_satisfaction(curr_user: Optional[str]) -> bool: +def _detect_satisfaction(curr_user: str | None) -> bool: if not curr_user: return False return any(p.search(curr_user) for p in _SATISFACTION_PATTERNS) -def _detect_failure(tool_results: List[Dict[str, Any]]) -> bool: +def _detect_failure(tool_results: list[dict[str, Any]]) -> bool: """Any tool result explicitly flagged as an error. We do NOT treat empty content as failure — many tools legitimately return @@ -173,7 +173,7 @@ def _detect_failure(tool_results: List[Dict[str, Any]]) -> bool: return False -def _signature(call: Dict[str, Any]) -> str: +def _signature(call: dict[str, Any]) -> str: """Stable signature for loop detection: name + sorted JSON-ish args.""" name = call.get("name") or call.get("function", {}).get("name", "") call_args = call.get("arguments") @@ -184,7 +184,7 @@ def _signature(call: Dict[str, Any]) -> str: return f"{name}({call_args})" -def _detect_loop(history: List[str], new_calls: List[Dict[str, Any]]) -> bool: +def _detect_loop(history: list[str], new_calls: list[dict[str, Any]]) -> bool: """Fires if any new call's signature appears >= LOOP_REPEAT_THRESHOLD-1 times in recent history (so this call would be the Nth).""" if not new_calls: @@ -209,7 +209,7 @@ _EXHAUSTION_KEYWORDS = ( ) -def _detect_exhaustion(status: Optional[int], tool_results: List[Dict[str, Any]]) -> bool: +def _detect_exhaustion(status: int | None, tool_results: list[dict[str, Any]]) -> bool: if status is not None and status in _EXHAUSTION_STATUSES: return True for r in tool_results: @@ -219,39 +219,53 @@ def _detect_exhaustion(status: Optional[int], tool_results: List[Dict[str, Any]] return False -# ---- Public entrypoint ---------------------------------------------------- +def detect_user_feedback( + previous_user_content: str | None, + current_user_content: str | None, + tool_results: list[dict[str, Any]], + allow_satisfaction: bool, +) -> SignalDelta: + return SignalDelta( + misalignment=int(_detect_misalignment(previous_user_content, current_user_content)), + disengagement=int(_detect_disengagement(current_user_content)), + satisfaction=int(allow_satisfaction and _detect_satisfaction(current_user_content)), + failure=int(_detect_failure(tool_results)), + ) -def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: - """ - Detect signals on this turn, mutate state, return the delta. +def detect_response_signals( + previous_assistant_content: str | None, + current_assistant_content: str | None, + tool_call_history: list[str], + tool_calls: list[dict[str, Any]], + tool_results: list[dict[str, Any]], + response_status: int | None, +) -> SignalDelta: + return SignalDelta( + stagnation=int( + _detect_stagnation( + previous_assistant_content, + current_assistant_content, + ) + ), + loop=int(_detect_loop(tool_call_history, tool_calls)), + exhaustion=int(_detect_exhaustion(response_status, tool_results)), + ) - O(1) per turn (no full-history rescan). Only inspects last_*, recent tool history - (which is bounded at TOOL_CALL_HISTORY_MAX), and the new turn payload. - """ - delta = SignalDelta() - if _detect_misalignment(state.last_user_content, turn.user_content): - delta.misalignment = 1 - if _detect_stagnation(state.last_assistant_content, turn.assistant_content): - delta.stagnation = 1 - if _detect_disengagement(turn.user_content): - delta.disengagement = 1 - if _detect_satisfaction(turn.user_content): - # Gate: only award satisfaction credit once per session, and only - # after MIN_TURNS_FOR_CLEAN_CREDIT turns of context. Early "thanks" - # on turn 1-2 is noise, not a validated quality signal. - current_turn_index = state.turn_count + 1 - if not state.clean_credit_awarded and current_turn_index >= MIN_TURNS_FOR_CLEAN_CREDIT: - delta.satisfaction = 1 - state.clean_credit_awarded = True - if _detect_failure(turn.tool_results): - delta.failure = 1 - if _detect_loop(state.tool_call_history, turn.tool_calls): - delta.loop = 1 - if _detect_exhaustion(turn.response_status, turn.tool_results): - delta.exhaustion = 1 +def merge_signal_deltas(*deltas: SignalDelta) -> SignalDelta: + return SignalDelta( + misalignment=sum(delta.misalignment for delta in deltas), + stagnation=sum(delta.stagnation for delta in deltas), + disengagement=sum(delta.disengagement for delta in deltas), + satisfaction=sum(delta.satisfaction for delta in deltas), + failure=sum(delta.failure for delta in deltas), + loop=sum(delta.loop for delta in deltas), + exhaustion=sum(delta.exhaustion for delta in deltas), + ) + +def apply_signal_delta(state: SessionState, delta: SignalDelta) -> None: state.misalignment_count += delta.misalignment state.stagnation_count += delta.stagnation state.disengagement_count += delta.disengagement @@ -260,6 +274,8 @@ def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: state.loop_count += delta.loop state.exhaustion_count += delta.exhaustion + +def advance_session_state(state: SessionState, turn: Turn) -> None: if turn.user_content: state.last_user_content = turn.user_content if turn.assistant_content: @@ -276,4 +292,38 @@ def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: state.turn_count += 1 state.last_processed_turn = state.turn_count + +# ---- Public entrypoint ---------------------------------------------------- + + +def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: + """ + Detect signals on this turn, mutate state, return the delta. + + O(1) per turn (no full-history rescan). Only inspects last_*, recent tool history + (which is bounded at TOOL_CALL_HISTORY_MAX), and the new turn payload. + """ + feedback_delta = detect_user_feedback( + state.last_user_content, + turn.user_content, + turn.tool_results, + allow_satisfaction=(not state.clean_credit_awarded and state.turn_count + 1 >= MIN_TURNS_FOR_CLEAN_CREDIT), + ) + response_delta = detect_response_signals( + state.last_assistant_content, + turn.assistant_content, + state.tool_call_history, + turn.tool_calls, + turn.tool_results, + turn.response_status, + ) + delta = merge_signal_deltas( + feedback_delta, + response_delta, + ) + apply_signal_delta(state, delta) + if delta.satisfaction: + state.clean_credit_awarded = True + advance_session_state(state, turn) + return delta diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 74644f01be8..bebdbba90ef 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -13,10 +13,12 @@ evaluated before either classification strategy and force a tier outright when m Inspired by ClawRouter: https://github.com/BlockRunAI/ClawRouter """ +from __future__ import annotations + import asyncio import random import re -from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast +from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast from pydantic import BaseModel @@ -38,6 +40,7 @@ if TYPE_CHECKING: from semantic_router.routers import SemanticRouter from litellm.router import Router + from litellm.router_strategy.adaptive_router.adaptive_router import AdaptiveRouter from litellm.types.router import PreRoutingHookResponse else: Router = Any @@ -63,7 +66,7 @@ Tiers: {prompt}""" -def _append_custom_keywords(base_keywords: list[str], custom_keywords: Optional[list[str]]) -> list[str]: +def _append_custom_keywords(base_keywords: list[str], custom_keywords: list[str] | None) -> list[str]: if not custom_keywords: return base_keywords base_lowered = frozenset(keyword.lower() for keyword in base_keywords) @@ -95,7 +98,7 @@ def _sanitize_user_api_key_auth(auth: Any) -> Any: return auth -def _classifier_call_metadata(metadata: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]: +def _classifier_call_metadata(metadata: dict[str, Any] | None) -> dict[str, Any] | None: if not metadata: return metadata return { @@ -110,7 +113,7 @@ class DimensionScore: __slots__ = ("name", "score", "signal") - def __init__(self, name: str, score: float, signal: Optional[str] = None): + def __init__(self, name: str, score: float, signal: str | None = None): self.name = name self.score = score self.signal = signal @@ -134,9 +137,9 @@ class ComplexityRouter(CustomLogger): def __init__( self, model_name: str, - litellm_router_instance: "Router", - complexity_router_config: Optional[Dict[str, Any]] = None, - default_model: Optional[str] = None, + litellm_router_instance: Router, + complexity_router_config: dict[str, Any] | None = None, + default_model: str | None = None, ): """ Initialize ComplexityRouter. @@ -173,7 +176,7 @@ class ComplexityRouter(CustomLogger): # embeddings are static, only the prompt is embedded per request). The lock # serializes the one-time build so concurrent cold-start requests don't each # construct the index and fire duplicate embedding calls. - self._semantic_routelayer: Optional[SemanticRouter] = None + self._semantic_routelayer: SemanticRouter | None = None self._semantic_routelayer_lock = asyncio.Lock() # Pre-compile regex patterns for efficiency @@ -185,6 +188,10 @@ class ComplexityRouter(CustomLogger): re.compile(r"[a-z]\)\s", re.IGNORECASE), ] + self.adaptive_router: AdaptiveRouter | None = None + self._model_tiers: dict[str, tuple[ComplexityTier, ...]] = {} + self._adaptive_init_attempted = False + verbose_router_logger.debug(f"ComplexityRouter initialized for {model_name} with tiers: {self.config.tiers}") def _estimate_tokens(self, text: str) -> int: @@ -228,12 +235,12 @@ class ComplexityRouter(CustomLogger): def _score_keyword_match( self, text: str, - keywords: List[str], + keywords: list[str], name: str, signal_label: str, - thresholds: Tuple[int, int], # (low, high) - scores: Tuple[float, float, float], # (none, low, high) - ) -> Tuple[DimensionScore, int]: + thresholds: tuple[int, int], # (low, high) + scores: tuple[float, float, float], # (none, low, high) + ) -> tuple[DimensionScore, int]: """Score based on keyword matches using word boundary matching. Returns: @@ -271,7 +278,7 @@ class ComplexityRouter(CustomLogger): return DimensionScore("questionComplexity", 0.5, f"{count} questions") return DimensionScore("questionComplexity", 0, None) - def classify(self, prompt: str, system_prompt: Optional[str] = None) -> Tuple[ComplexityTier, float, List[str]]: + def classify(self, prompt: str, system_prompt: str | None = None) -> tuple[ComplexityTier, float, list[str]]: """ Classify a prompt by complexity. @@ -330,7 +337,7 @@ class ComplexityRouter(CustomLogger): (0, -1.0, -1.0), ) - dimensions: List[DimensionScore] = [ + dimensions: list[DimensionScore] = [ self._score_token_count(estimated_tokens), code_score, reasoning_score, @@ -372,8 +379,8 @@ class ComplexityRouter(CustomLogger): async def aclassify( self, prompt: str, - system_prompt: Optional[str] = None, - request_kwargs: Optional[dict[str, Any]] = None, + system_prompt: str | None = None, + request_kwargs: dict[str, Any] | None = None, ) -> tuple[ComplexityTier, float, list[str]]: """ Classify a prompt by complexity, using the LLM classifier when configured. @@ -396,8 +403,8 @@ class ComplexityRouter(CustomLogger): async def _classify_with_llm( self, prompt: str, - system_prompt: Optional[str] = None, - request_kwargs: Optional[dict[str, Any]] = None, + system_prompt: str | None = None, + request_kwargs: dict[str, Any] | None = None, ) -> ComplexityTier: """Call the configured classifier model and parse its structured tier response.""" llm_config = self.config.classifier_llm_config @@ -458,7 +465,176 @@ class ComplexityRouter(CustomLogger): raise ValueError(f"Empty model pool for tier {tier_key}") return random.choice(model) - def _lexical_tier_override(self, user_message: str) -> Optional[ComplexityTier]: + def _tier_pools(self) -> dict[str, list[str]]: + return {tier: (models if isinstance(models, list) else [models]) for tier, models in self.config.tiers.items()} + + def _ensure_adaptive_router(self) -> Any | None: + if not self.config.adaptive: + return None + if self.adaptive_router is not None: + return self.adaptive_router + if self._adaptive_init_attempted: + return self.adaptive_router + self._adaptive_init_attempted = True + + from litellm.router_strategy.adaptive_router.adaptive_router import ( + AdaptiveRouter, + ) + from litellm.router_strategy.adaptive_router.config import ( + ADAPTIVE_ROUTER_CHOSEN_MODEL_KEY, + ) + from litellm.types.router import ( + AdaptiveRouterConfig, + AdaptiveRouterPreferences, + ) + + pools = self._tier_pools() + available_models = list(dict.fromkeys(model for models in pools.values() for model in models)) + self._model_tiers = { + model: tuple(ComplexityTier(tier_name) for tier_name, models in pools.items() if model in models) + for model in available_models + } + + model_to_prefs: dict[str, AdaptiveRouterPreferences] = {} + model_to_cost: dict[str, float] = {} + model_list = getattr(self.litellm_router_instance, "model_list", None) or [] + name_to_indices = getattr(self.litellm_router_instance, "model_name_to_deployment_indices", {}) or {} + for name in available_models: + indices = name_to_indices.get(name, []) + if not indices: + model_to_prefs[name] = AdaptiveRouterPreferences(quality_tier=2, strengths=[]) + model_to_cost[name] = 0.0 + continue + deployment = model_list[indices[0]] + mi = deployment.get("model_info") if isinstance(deployment, dict) else deployment.model_info + mi_dict: dict[str, Any] = mi if isinstance(mi, dict) else (mi.model_dump() if mi else {}) + prefs_raw = mi_dict.get("adaptive_router_preferences") + if prefs_raw is not None: + model_to_prefs[name] = AdaptiveRouterPreferences(**prefs_raw) + else: + model_to_prefs[name] = AdaptiveRouterPreferences(quality_tier=2, strengths=[]) + + lp = deployment.get("litellm_params") if isinstance(deployment, dict) else deployment.litellm_params + lp_dict: dict[str, Any] = lp if isinstance(lp, dict) else (lp.model_dump() if lp else {}) + cost = lp_dict.get("input_cost_per_token") + model_to_cost[name] = float(cost) if cost is not None else 0.0 + + self.adaptive_router = AdaptiveRouter( + router_name=self.model_name, + config=AdaptiveRouterConfig( + available_models=available_models, + weights=self.config.adaptive_weights, + ), + model_to_prefs=model_to_prefs, + model_to_cost=model_to_cost, + ) + self._adaptive_chosen_model_key = ADAPTIVE_ROUTER_CHOSEN_MODEL_KEY + return self.adaptive_router + + def _soft_floor_pick( + self, + classified_tier: ComplexityTier, + user_message: str, + request_kwargs: dict[str, Any] | None = None, + ) -> str: + from litellm.router_strategy.adaptive_router.bandit import ( + normalized_cost, + thompson_sample, + ) + from litellm.router_strategy.adaptive_router.classifier import classify_prompt + + adaptive = self._ensure_adaptive_router() + if adaptive is None: + return self.get_model_for_tier(classified_tier) + + request_type = classify_prompt(user_message) + classified_idx = TIER_SEVERITY_ORDER.index(classified_tier) + pools = self._tier_pools() + classified_candidates = tuple(pools.get(classified_tier.value, ())) + cold_start_candidates = tuple( + model for model in classified_candidates if adaptive._cells[(request_type, model)].total_samples == 0 + ) + if cold_start_candidates: + chosen_model = random.choice(cold_start_candidates) + if request_kwargs is not None: + metadata = request_kwargs.setdefault("metadata", {}) + if isinstance(metadata, dict): + metadata["adaptive_router_decision"] = { + "phase": "cold_start", + "classified_tier": classified_tier.value, + "request_type": request_type.value, + "eligible_mode": "classified_tier", + "quality_weight": self.config.adaptive_weights.quality, + "cost_weight": self.config.adaptive_weights.cost, + "tier_distance_penalty": self.config.tier_distance_penalty, + "chosen_model": chosen_model, + "candidates": [ + { + "model": model, + "total_samples": adaptive._cells[(request_type, model)].total_samples, + } + for model in cold_start_candidates + ], + } + return chosen_model + if self.config.adaptive_eligible == "classified_tier": + candidates = list(classified_candidates) + if not candidates: + return self.get_model_for_tier(classified_tier) + else: + candidates = list(adaptive.config.available_models) + + all_costs = [adaptive.model_to_cost.get(m, 0.0) for m in candidates] + quality_weight = self.config.adaptive_weights.quality + cost_weight = self.config.adaptive_weights.cost + penalty_weight = self.config.tier_distance_penalty + + best_model: str | None = None + best_score = float("-inf") + candidate_scores: list[dict[str, Any]] = [] + for model in candidates: + cell = adaptive._cells[(request_type, model)] + quality_sample = thompson_sample(cell) + cost_score = normalized_cost(adaptive.model_to_cost.get(model, 0.0), all_costs) + if self.config.adaptive_eligible == "classified_tier": + distance = 0 + else: + model_tiers = self._model_tiers.get(model, (classified_tier,)) + distance = min( + abs(TIER_SEVERITY_ORDER.index(model_tier) - classified_idx) for model_tier in model_tiers + ) + score = quality_weight * quality_sample + cost_weight * cost_score - penalty_weight * distance + candidate_scores.append( + { + "model": model, + "quality_sample": quality_sample, + "cost_score": cost_score, + "tier_distance": distance, + "score": score, + } + ) + if score > best_score: + best_score = score + best_model = model + if best_model is None: + return self.get_model_for_tier(classified_tier) + if request_kwargs is not None: + metadata = request_kwargs.setdefault("metadata", {}) + if isinstance(metadata, dict): + metadata["adaptive_router_decision"] = { + "phase": "adaptive", + "classified_tier": classified_tier.value, + "request_type": request_type.value, + "eligible_mode": self.config.adaptive_eligible, + "quality_weight": quality_weight, + "cost_weight": cost_weight, + "tier_distance_penalty": penalty_weight, + "chosen_model": best_model, + "candidates": candidate_scores, + } + return best_model + + def _lexical_tier_override(self, user_message: str) -> ComplexityTier | None: """When keyword_tier_rules match literally, the most-severe matched tier wins. Escalating to the highest tier (rather than the first rule in the list) keeps @@ -476,7 +652,7 @@ class ComplexityRouter(CustomLogger): return None return max(matched_tiers, key=TIER_SEVERITY_ORDER.index) - def _get_or_create_semantic_routelayer(self) -> "SemanticRouter": + def _get_or_create_semantic_routelayer(self) -> SemanticRouter: """Build (once) a SemanticRouter with one route per tier, utterances = that tier's keywords.""" if self._semantic_routelayer is not None: return self._semantic_routelayer @@ -515,7 +691,7 @@ class ComplexityRouter(CustomLogger): self._semantic_routelayer = routelayer return routelayer - async def _ensure_semantic_routelayer(self) -> "SemanticRouter": + async def _ensure_semantic_routelayer(self) -> SemanticRouter: """Return the cached route layer, building it once under a lock if needed. The build embeds the static route utterances via the encoder's synchronous path, @@ -531,7 +707,7 @@ class ComplexityRouter(CustomLogger): routelayer = await asyncio.to_thread(self._get_or_create_semantic_routelayer) return routelayer - async def _semantic_tier_override(self, user_message: str, request_kwargs: Dict) -> Optional[ComplexityTier]: + async def _semantic_tier_override(self, user_message: str, request_kwargs: dict) -> ComplexityTier | None: """Match the prompt against keyword_tier_rules by embedding similarity. Embeds the query ourselves (instead of letting SemanticRouter.acall embed it @@ -571,7 +747,7 @@ class ComplexityRouter(CustomLogger): except ValueError: return None - async def _resolve_keyword_tier_override(self, user_message: str, request_kwargs: Dict) -> Optional[ComplexityTier]: + async def _resolve_keyword_tier_override(self, user_message: str, request_kwargs: dict) -> ComplexityTier | None: """Resolve a keyword_tier_rule override, semantically or lexically per config. Returns None (no override -> fall through to the scorer) not only when no rule @@ -592,9 +768,9 @@ class ComplexityRouter(CustomLogger): def _resolve_messages( self, - messages: Optional[List[Dict[str, Any]]], - request_kwargs: Dict, - ) -> Optional[List[Dict[str, Any]]]: + messages: list[dict[str, Any]] | None, + request_kwargs: dict, + ) -> list[dict[str, Any]] | None: """ Resolve messages from the request, converting from other formats if needed. @@ -609,11 +785,11 @@ class ComplexityRouter(CustomLogger): @staticmethod def _extract_user_message_and_system_prompt( - messages: List[Dict[str, Any]], - ) -> Tuple[Optional[str], Optional[str]]: + messages: list[dict[str, Any]], + ) -> tuple[str | None, str | None]: """Extract the last user message text and last system prompt from messages.""" - user_message: Optional[str] = None - system_prompt: Optional[str] = None + user_message: str | None = None + system_prompt: str | None = None for msg in reversed(messages): role = msg.get("role", "") @@ -636,11 +812,11 @@ class ComplexityRouter(CustomLogger): async def async_pre_routing_hook( self, model: str, - request_kwargs: Dict, - messages: Optional[List[Dict[str, Any]]] = None, - input: Optional[Union[str, List]] = None, - specific_deployment: Optional[bool] = False, - ) -> Optional["PreRoutingHookResponse"]: + request_kwargs: dict, + messages: list[dict[str, Any]] | None = None, + input: Union[str, list] | None = None, + specific_deployment: bool | None = False, + ) -> Optional[PreRoutingHookResponse]: """ Pre-routing hook called before the routing decision. @@ -692,12 +868,25 @@ class ComplexityRouter(CustomLogger): ) tier, score, signals = await self.aclassify(user_message, system_prompt, request_kwargs) - routed_model = self.get_model_for_tier(tier) - - verbose_router_logger.info( - f"ComplexityRouter: routing decision cause=complexity_scorer, tier={tier.value}, " - f"score={score:.3f}, signals={signals}, routed_model={routed_model}" - ) + if self.config.adaptive: + routed_model = self._soft_floor_pick(tier, user_message, request_kwargs) + adaptive = self._ensure_adaptive_router() + if adaptive is not None: + kwargs_metadata = request_kwargs.setdefault("metadata", {}) + if isinstance(kwargs_metadata, dict): + chosen_key = getattr(self, "_adaptive_chosen_model_key", "adaptive_router_chosen_model") + kwargs_metadata[chosen_key] = routed_model + verbose_router_logger.info( + f"ComplexityRouter[adaptive]: routing decision cause=complexity_scorer, " + f"tier={tier.value}, score={score:.3f}, " + f"signals={signals}, routed_model={routed_model}" + ) + else: + routed_model = self.get_model_for_tier(tier) + verbose_router_logger.info( + f"ComplexityRouter: routing decision cause=complexity_scorer, tier={tier.value}, " + f"score={score:.3f}, signals={signals}, routed_model={routed_model}" + ) return PreRoutingHookResponse( model=routed_model, diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 8c8e5acb51f..df699d1a059 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -6,10 +6,12 @@ All values are configurable via proxy config.yaml. """ from enum import Enum -from typing import Dict, List, Literal, Optional +from typing import Literal from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator +from litellm.types.router import AdaptiveRouterWeights + class ComplexityTier(str, Enum): """Complexity tiers for routing decisions.""" @@ -27,11 +29,13 @@ TIER_SEVERITY_ORDER: tuple[ComplexityTier, ...] = ( ComplexityTier.REASONING, ) +DEFAULT_TIER_DISTANCE_PENALTY: float = 0.5 + class KeywordTierRule(BaseModel): """A deterministic override: if any keyword matches, route to this tier.""" - keywords: List[str] = Field( + keywords: list[str] = Field( min_length=1, description="Keywords/phrases that trigger this rule (lexical or semantic match)", ) @@ -56,7 +60,7 @@ class KeywordTierRule(BaseModel): # Note: Keywords should be full words/phrases to avoid substring false positives. # The matching logic uses word boundary detection for single-word keywords. -DEFAULT_CODE_KEYWORDS: List[str] = [ +DEFAULT_CODE_KEYWORDS: list[str] = [ "function", "class", "def", @@ -104,7 +108,7 @@ DEFAULT_CODE_KEYWORDS: List[str] = [ "pull request", ] -DEFAULT_REASONING_KEYWORDS: List[str] = [ +DEFAULT_REASONING_KEYWORDS: list[str] = [ "step by step", "think through", "let's think", @@ -126,7 +130,7 @@ DEFAULT_REASONING_KEYWORDS: List[str] = [ "conclude", ] -DEFAULT_TECHNICAL_KEYWORDS: List[str] = [ +DEFAULT_TECHNICAL_KEYWORDS: list[str] = [ "architecture", "distributed", "scalable", @@ -158,7 +162,7 @@ DEFAULT_TECHNICAL_KEYWORDS: List[str] = [ # Note: "async", "kubernetes", "docker" are in DEFAULT_CODE_KEYWORDS ] -DEFAULT_SIMPLE_KEYWORDS: List[str] = [ +DEFAULT_SIMPLE_KEYWORDS: list[str] = [ "what is", "what's", "define", @@ -191,7 +195,7 @@ DEFAULT_SIMPLE_KEYWORDS: List[str] = [ # ─── Default Dimension Weights ─── -DEFAULT_DIMENSION_WEIGHTS: Dict[str, float] = { +DEFAULT_DIMENSION_WEIGHTS: dict[str, float] = { "tokenCount": 0.10, # Reduced - length is less important than content "codePresence": 0.30, # High - code requests need capable models "reasoningMarkers": 0.25, # High - explicit reasoning requests @@ -204,7 +208,7 @@ DEFAULT_DIMENSION_WEIGHTS: Dict[str, float] = { # ─── Default Tier Boundaries ─── -DEFAULT_TIER_BOUNDARIES: Dict[str, float] = { +DEFAULT_TIER_BOUNDARIES: dict[str, float] = { "simple_medium": 0.15, # Lower threshold to catch more MEDIUM cases "medium_complex": 0.35, # Lower threshold to catch technical COMPLEX cases "complex_reasoning": 0.60, # Reasoning tier reserved for explicit reasoning markers @@ -213,7 +217,7 @@ DEFAULT_TIER_BOUNDARIES: Dict[str, float] = { # ─── Default Token Thresholds ─── -DEFAULT_TOKEN_THRESHOLDS: Dict[str, int] = { +DEFAULT_TOKEN_THRESHOLDS: dict[str, int] = { "simple": 15, # Only very short prompts (<15 tokens) are penalized "complex": 400, # Long prompts (>400 tokens) get complexity boost } @@ -221,7 +225,7 @@ DEFAULT_TOKEN_THRESHOLDS: Dict[str, int] = { # ─── Default Tier to Model Mapping ─── -DEFAULT_TIER_MODELS: Dict[str, str] = { +DEFAULT_TIER_MODELS: dict[str, str] = { "SIMPLE": "gpt-4o-mini", "MEDIUM": "gpt-4o", "COMPLEX": "claude-sonnet-4-20250514", @@ -244,46 +248,47 @@ class ClassifierLLMConfig(BaseModel): class ComplexityRouterConfig(BaseModel): """Configuration for the ComplexityRouter.""" - # string = pin; list = random pick from the tier pool + # string = pin; list = random pick when adaptive=False, soft-floor home pool when adaptive=True tiers: dict[str, str | list[str]] = Field( default_factory=lambda: DEFAULT_TIER_MODELS.copy(), description=( - "Mapping of complexity tiers to a model or model pool. A list is randomly picked from for that tier" + "Mapping of complexity tiers to a model or model pool. " + "A list is randomly picked from when adaptive=False, and used as a soft-floor home pool when adaptive=True" ), ) # Tier boundaries (normalized scores) - tier_boundaries: Dict[str, float] = Field( + tier_boundaries: dict[str, float] = Field( default_factory=lambda: DEFAULT_TIER_BOUNDARIES.copy(), description="Score boundaries between tiers", ) # Token count thresholds - token_thresholds: Dict[str, int] = Field( + token_thresholds: dict[str, int] = Field( default_factory=lambda: DEFAULT_TOKEN_THRESHOLDS.copy(), description="Token count thresholds for simple/complex classification", ) # Dimension weights - dimension_weights: Dict[str, float] = Field( + dimension_weights: dict[str, float] = Field( default_factory=lambda: DEFAULT_DIMENSION_WEIGHTS.copy(), description="Weights for each scoring dimension", ) # Keyword lists (overridable) - code_keywords: Optional[List[str]] = Field( + code_keywords: list[str] | None = Field( default=None, description="Keywords indicating code-related content", ) - reasoning_keywords: Optional[List[str]] = Field( + reasoning_keywords: list[str] | None = Field( default=None, description="Keywords indicating reasoning-required content", ) - technical_keywords: Optional[List[str]] = Field( + technical_keywords: list[str] | None = Field( default=None, description="Keywords indicating technical content", ) - custom_technical_keywords: Optional[list[str]] = Field( + custom_technical_keywords: list[str] | None = Field( default=None, description=( "Domain-specific technical keywords appended to the effective base list " @@ -292,13 +297,13 @@ class ComplexityRouterConfig(BaseModel): "the base list and within this list." ), ) - simple_keywords: Optional[List[str]] = Field( + simple_keywords: list[str] | None = Field( default=None, description="Keywords indicating simple/basic queries", ) # Default model if scoring fails - default_model: Optional[str] = Field( + default_model: str | None = Field( default=None, description="Default model to use if tier cannot be determined", ) @@ -308,13 +313,34 @@ class ComplexityRouterConfig(BaseModel): default="heuristic", description="Classification strategy: local regex/keyword scoring, or an LLM call", ) - classifier_llm_config: Optional[ClassifierLLMConfig] = Field( + classifier_llm_config: ClassifierLLMConfig | None = Field( default=None, description="Configuration for the LLM classifier; required when classifier_type is 'llm'", ) + adaptive: bool = Field( + default=False, + description="Enable adaptive bandit selection with soft complexity floors", + ) + adaptive_weights: AdaptiveRouterWeights = Field( + default_factory=lambda: AdaptiveRouterWeights(quality=0.3, cost=0.7), + description="Quality vs cost weights for adaptive selection (used when adaptive=True)", + ) + tier_distance_penalty: float = Field( + default=DEFAULT_TIER_DISTANCE_PENALTY, + ge=0.0, + description="Score penalty per tier-step away from the classified tier when adaptive=True", + ) + adaptive_eligible: Literal["all", "classified_tier"] = Field( + default="all", + description=( + "When adaptive=True: 'all' scores every pool model with a tier-distance penalty (soft floors); " + "'classified_tier' Thompson-samples only inside the classified tier's pool" + ), + ) + # Deterministic keyword -> tier overrides, evaluated before weighted scoring - keyword_tier_rules: Optional[List[KeywordTierRule]] = Field( + keyword_tier_rules: list[KeywordTierRule] | None = Field( default=None, description="Rules that force a specific tier when their keywords match the prompt", ) @@ -324,7 +350,7 @@ class ComplexityRouterConfig(BaseModel): default=False, description="Match keyword_tier_rules by embedding similarity instead of literal text", ) - embedding_model: Optional[str] = Field( + embedding_model: str | None = Field( default=None, description="Embedding model (LiteLLM model name) used when semantic_keyword_matching is enabled", ) @@ -358,6 +384,19 @@ class ComplexityRouterConfig(BaseModel): raise ValueError("classifier_llm_config is required when classifier_type is 'llm'") return self + @model_validator(mode="after") + def _validate_adaptive_pools(self) -> "ComplexityRouterConfig": + if not self.adaptive: + return self + normalized = {tier: (models if isinstance(models, list) else [models]) for tier, models in self.tiers.items()} + if not any(normalized.values()): + raise ValueError("adaptive=True requires at least one non-empty tier pool") + empty = [tier for tier, models in normalized.items() if not models] + if empty: + raise ValueError(f"adaptive=True tier pools must be non-empty; empty tiers: {empty}") + self.tiers = normalized + return self + @model_validator(mode="after") def _validate_semantic_matching(self) -> "ComplexityRouterConfig": if not self.semantic_keyword_matching: diff --git a/ruff-strict-budget.json b/ruff-strict-budget.json index 7750ac6628a..dcde6fd1641 100644 --- a/ruff-strict-budget.json +++ b/ruff-strict-budget.json @@ -306,7 +306,7 @@ "limit": 9 }, "TID251": { - "limit": 2710 + "limit": 2701 }, "TRY002": { "limit": 548 @@ -324,7 +324,7 @@ "limit": 883 }, "UP006": { - "limit": 12869 + "limit": 12792 }, "UP007": { "limit": 2570 @@ -354,7 +354,7 @@ "limit": 4 }, "UP035": { - "limit": 2295 + "limit": 2284 }, "UP036": { "limit": 4 @@ -363,6 +363,6 @@ "limit": 105 }, "UP045": { - "limit": 18517 + "limit": 18462 } } diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py b/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py index 93c4db90dad..cbf5635a5ae 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py @@ -7,9 +7,6 @@ from litellm.router_strategy.adaptive_router import adaptive_router as ar_module import pytest from litellm.router_strategy.adaptive_router.adaptive_router import AdaptiveRouter -from litellm.router_strategy.adaptive_router.config import ( - OWNER_CACHE_TTL_SECONDS, -) from litellm.router_strategy.adaptive_router.signals import Turn from litellm.types.router import ( AdaptiveRouterConfig, @@ -22,9 +19,7 @@ def _make_router() -> AdaptiveRouter: cfg = AdaptiveRouterConfig(available_models=["fast", "smart"]) prefs = { "fast": AdaptiveRouterPreferences(quality_tier=1, strengths=[]), - "smart": AdaptiveRouterPreferences( - quality_tier=3, strengths=[RequestType.CODE_GENERATION] - ), + "smart": AdaptiveRouterPreferences(quality_tier=3, strengths=[RequestType.CODE_GENERATION]), } costs = {"fast": 0.0001, "smart": 0.001} return AdaptiveRouter( @@ -58,85 +53,6 @@ async def test_pick_model_min_quality_tier_filter_raises_when_no_eligible(): await r.pick_model(RequestType.GENERAL, min_quality_tier=4) -@pytest.mark.asyncio -async def test_pick_model_is_stateless_no_owner_cache_writes(): - """pick_model must not touch the owner cache — that's gated post-call.""" - r = _make_router() - for _ in range(5): - await r.pick_model(RequestType.GENERAL) - assert r._owner_cache == {} - - -# ---- claim_or_check_owner ----------------------------------------------- - - -def test_claim_or_check_owner_first_call_claims_and_returns_true(monkeypatch): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - - assert r.claim_or_check_owner("sess-A", "fast") is True - assert r._owner_cache["sess-A"] == ("fast", 1_000.0 + OWNER_CACHE_TTL_SECONDS) - assert r._skipped_updates_total == 0 - - -def test_claim_or_check_owner_same_model_returns_true_without_extending_ttl( - monkeypatch, -): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - r.claim_or_check_owner("sess-A", "fast") - original_expiry = r._owner_cache["sess-A"][1] - - monkeypatch.setattr(ar_module.time, "time", lambda: 1_500.0) - assert r.claim_or_check_owner("sess-A", "fast") is True - # No extension on hit — owner cache snapshots the first claim. - assert r._owner_cache["sess-A"][1] == original_expiry - - -def test_claim_or_check_owner_mismatch_skips_and_increments_counter(monkeypatch): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - r.claim_or_check_owner("sess-A", "fast") - - assert r.claim_or_check_owner("sess-A", "smart") is False - assert r._skipped_updates_total == 1 - # Owner unchanged. - assert r._owner_cache["sess-A"][0] == "fast" - - -def test_claim_or_check_owner_expired_owner_reclaims_for_new_model(monkeypatch): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - r.claim_or_check_owner("sess-A", "fast") - - monkeypatch.setattr( - ar_module.time, "time", lambda: 1_000.0 + OWNER_CACHE_TTL_SECONDS + 1 - ) - assert r.claim_or_check_owner("sess-A", "smart") is True - assert r._owner_cache["sess-A"][0] == "smart" - # Reclaim isn't a skip. - assert r._skipped_updates_total == 0 - - -def test_owner_cache_evicts_expired_entries_when_threshold_crossed(monkeypatch): - """Past _OWNER_CACHE_SWEEP_THRESHOLD live entries, new claims sweep stale.""" - r = _make_router() - monkeypatch.setattr(ar_module, "_OWNER_CACHE_SWEEP_THRESHOLD", 5) - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - for i in range(5): - r.claim_or_check_owner(f"old-{i}", "fast") - assert len(r._owner_cache) == 5 - - # Jump past TTL so all "old-*" entries are now expired. - monkeypatch.setattr( - ar_module.time, "time", lambda: 1_000.0 + OWNER_CACHE_TTL_SECONDS + 1 - ) - r.claim_or_check_owner("new-1", "fast") - # Sweep ran -> only the new entry remains. - assert "new-1" in r._owner_cache - assert all(k.startswith("new-") for k in r._owner_cache) - - # ---- record_turn -------------------------------------------------------- @@ -185,9 +101,7 @@ async def test_record_turn_satisfaction_increments_alpha(): # Prime with 2 prior turns to clear the MIN_TURNS_FOR_CLEAN_CREDIT gate. # Use distinct content to avoid incidentally firing stagnation/misalignment. priming_turns = [ - Turn( - user_content="alpha bravo charlie", assistant_content="delta echo foxtrot" - ), + Turn(user_content="alpha bravo charlie", assistant_content="delta echo foxtrot"), Turn( user_content="golf hotel india juliet", assistant_content="kilo lima mike november", @@ -232,6 +146,128 @@ async def test_record_turn_failure_increments_beta(): assert cell_after.alpha == pytest.approx(cell_before.alpha) +@pytest.mark.asyncio +async def test_record_turn_detects_exhaustion_in_tool_results(): + r = _make_router() + + delta = await r.record_turn( + session_id="exhausted", + model_name="smart", + request_type=RequestType.GENERAL, + turn=Turn(tool_results=[{"content": "rate limit exceeded"}]), + ) + + assert delta.exhaustion == 1 + assert r._session_states[("exhausted", "smart")].exhaustion_count == 1 + + +@pytest.mark.asyncio +async def test_record_turn_attributes_user_feedback_to_previous_response_model(): + r = _make_router() + fast_before = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_before = r._cells[(RequestType.GENERAL, "smart")] + + await r.record_turn( + session_id="feedback-switch", + model_name="fast", + request_type=RequestType.CODE_GENERATION, + turn=Turn( + user_content="fix this python retry bug", + assistant_content="clear the cache on every retry", + ), + ) + await r.record_turn( + session_id="feedback-switch", + model_name="smart", + request_type=RequestType.GENERAL, + turn=Turn( + user_content="the python fix is still broken", + assistant_content="keep successful cache entries", + ), + ) + + fast_after = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_after = r._cells[(RequestType.GENERAL, "smart")] + assert fast_after.beta == pytest.approx(fast_before.beta + 1.0) + assert smart_after.beta == pytest.approx(smart_before.beta) + snapshot = await r.get_state_snapshot() + assert snapshot["feedback_attributed_total"] == 1 + assert snapshot["cross_model_feedback_total"] == 1 + assert snapshot["feedback_without_context_total"] == 0 + + +@pytest.mark.asyncio +async def test_record_turn_attributes_satisfaction_to_previous_response_model(): + r = _make_router() + await r.record_turn( + session_id="satisfaction-switch", + model_name="smart", + request_type=RequestType.CODE_GENERATION, + turn=Turn( + user_content="write a python retry helper", + assistant_content="first draft", + ), + ) + await r.record_turn( + session_id="satisfaction-switch", + model_name="fast", + request_type=RequestType.CODE_GENERATION, + turn=Turn( + user_content="add exponential backoff to the python helper", + assistant_content="updated draft", + ), + ) + fast_before = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_before = r._cells[(RequestType.GENERAL, "smart")] + + await r.record_turn( + session_id="satisfaction-switch", + model_name="smart", + request_type=RequestType.GENERAL, + turn=Turn( + user_content="thanks, that worked", + assistant_content="glad to help", + ), + ) + + fast_after = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_after = r._cells[(RequestType.GENERAL, "smart")] + assert fast_after.alpha == pytest.approx(fast_before.alpha + 1.0) + assert smart_after.alpha == pytest.approx(smart_before.alpha) + + +@pytest.mark.asyncio +async def test_record_turn_bounds_feedback_contexts_and_evicts_least_recent_session(): + r = _make_router() + context_limit = ar_module._FEEDBACK_CONTEXT_MAX_ENTRIES + + for index in range(context_limit): + await r.record_turn( + session_id=f"session-{index}", + model_name="fast", + request_type=RequestType.GENERAL, + turn=Turn(user_content="question", assistant_content="answer"), + ) + + await r.record_turn( + session_id="session-0", + model_name="fast", + request_type=RequestType.GENERAL, + turn=Turn(user_content="follow up", assistant_content="updated answer"), + ) + await r.record_turn( + session_id="overflow", + model_name="fast", + request_type=RequestType.GENERAL, + turn=Turn(user_content="question", assistant_content="answer"), + ) + + assert len(r._feedback_contexts) == context_limit + assert "session-0" in r._feedback_contexts + assert "session-1" not in r._feedback_contexts + assert "overflow" in r._feedback_contexts + + @pytest.mark.asyncio async def test_load_state_from_db_overrides_cold_start(): r = _make_router() @@ -270,9 +306,7 @@ async def test_load_state_from_db_handles_unknown_request_type(): good_row.beta = 3.0 prisma = MagicMock() - prisma.db.litellm_adaptiverouterstate.find_many = AsyncMock( - return_value=[bad_row, good_row] - ) + prisma.db.litellm_adaptiverouterstate.find_many = AsyncMock(return_value=[bad_row, good_row]) await r.load_state_from_db(prisma) # Unknown skipped; good applied. diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py b/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py index 9786832b4ae..3071f916ef1 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py @@ -110,23 +110,6 @@ async def test_pick_record_flush_full_cycle(): assert session_call.kwargs["data"]["create"]["model_name"] == chosen -@pytest.mark.asyncio -async def test_owner_cache_pins_attribution_to_first_picked_model(): - """First call claims ownership; matching model returns True, mismatch False.""" - router = _make_router() - chosen = await router.pick_model(RequestType.GENERAL) - assert router.claim_or_check_owner("sess-own", chosen) is True - - # Same model on later turns keeps attributing. - for _ in range(5): - assert router.claim_or_check_owner("sess-own", chosen) is True - - # A different model on a later turn is rejected. - other = "gpt-4o" if chosen == "gpt-4o-mini" else "gpt-4o-mini" - assert router.claim_or_check_owner("sess-own", other) is False - assert router._skipped_updates_total == 1 - - @pytest.mark.asyncio async def test_pick_model_returns_valid_models_without_error(): router = _make_router() diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py b/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py index a2b85f2ce53..ad61f43c5a0 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py @@ -16,10 +16,9 @@ from litellm.router_strategy.adaptive_router.hooks import ( from litellm.router_strategy.adaptive_router.signals import Turn -def _make_hook(claim: bool = True) -> AdaptiveRouterPostCallHook: +def _make_hook() -> AdaptiveRouterPostCallHook: fake_router = MagicMock() fake_router.record_turn = AsyncMock() - fake_router.claim_or_check_owner = MagicMock(return_value=claim) return AdaptiveRouterPostCallHook(adaptive_router=fake_router) @@ -151,7 +150,24 @@ async def test_hook_skips_when_below_signal_gate(): kwargs = _kwargs(messages=short) await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) hook.adaptive_router.record_turn.assert_not_awaited() - hook.adaptive_router.claim_or_check_owner.assert_not_called() + + +@pytest.mark.asyncio +async def test_hook_tracks_short_conversation_with_explicit_session_id(): + hook = _make_hook() + kwargs = _kwargs( + messages=[{"role": "user", "content": "hi"}], + extra_litellm_params={"litellm_session_id": "explicit-short"}, + ) + await hook.async_log_success_event( + kwargs, + _resp_with_content("hello"), + 0.0, + 1.0, + ) + assert hook.adaptive_router.record_turn.await_args.kwargs["session_id"] == ( + "explicit-short" + ) @pytest.mark.asyncio @@ -168,22 +184,19 @@ async def test_hook_skips_when_chosen_model_missing_from_metadata(): kwargs = _kwargs(chosen=None) await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) hook.adaptive_router.record_turn.assert_not_awaited() - hook.adaptive_router.claim_or_check_owner.assert_not_called() @pytest.mark.asyncio -async def test_hook_skips_when_owner_cache_mismatch(): - """A different model owns this conversation -> no attribution.""" - hook = _make_hook(claim=False) +async def test_hook_records_when_model_changes(): + hook = _make_hook() kwargs = _kwargs(chosen="fast") await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) - hook.adaptive_router.claim_or_check_owner.assert_called_once() - hook.adaptive_router.record_turn.assert_not_awaited() + hook.adaptive_router.record_turn.assert_awaited_once() @pytest.mark.asyncio -async def test_hook_records_turn_when_owner_claims(): - hook = _make_hook(claim=True) +async def test_hook_records_turn(): + hook = _make_hook() kwargs = _kwargs(chosen="smart", messages=_long_messages("ask")) await hook.async_log_success_event( kwargs, _resp_with_content("answer here"), 0.0, 1.0 @@ -205,8 +218,6 @@ async def test_hook_uses_explicit_session_id_when_provided(): extra_litellm_params={"litellm_session_id": "explicit-sess"}, ) await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) - args, _ = hook.adaptive_router.claim_or_check_owner.call_args - assert args[0] == "explicit-sess" assert hook.adaptive_router.record_turn.await_args.kwargs["session_id"] == ( "explicit-sess" ) diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py b/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py index 753a449791b..d6d89c8e811 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py @@ -1,7 +1,6 @@ """Tests for the GET /adaptive_router/state introspection endpoint and the underlying `AdaptiveRouter.get_state_snapshot()` helper.""" -import time from unittest.mock import MagicMock import pytest @@ -9,7 +8,7 @@ from fastapi import HTTPException from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.router_strategy.adaptive_router.adaptive_router import AdaptiveRouter -from litellm.router_strategy.adaptive_router.bandit import BanditCell, apply_delta +from litellm.router_strategy.adaptive_router.bandit import apply_delta from litellm.types.router import ( AdaptiveRouterConfig, AdaptiveRouterPreferences, @@ -47,8 +46,6 @@ async def test_get_state_snapshot_returns_cell_per_request_type_per_model(): assert snap["available_models"] == ["fast", "smart"] assert snap["weights"] == {"quality": 0.7, "cost": 0.3} assert snap["model_costs"] == {"fast": 0.0001, "smart": 0.001} - assert snap["owner_cache_live"] == 0 - assert snap["skipped_updates_total"] == 0 assert set(snap["queue"].keys()) == { "state_pending", "session_pending", @@ -95,26 +92,6 @@ async def test_get_state_snapshot_quality_mean_matches_alpha_over_total(): assert cell["quality_mean"] == pytest.approx(expected_mean) -@pytest.mark.asyncio -async def test_get_state_snapshot_counts_only_live_owner_cache_entries(): - r = _make_router() - now = time.time() - r._owner_cache["live-1"] = ("fast", now + 3600) - r._owner_cache["live-2"] = ("smart", now + 3600) - r._owner_cache["expired-1"] = ("fast", now - 1) - - snap = await r.get_state_snapshot() - assert snap["owner_cache_live"] == 2 - - -@pytest.mark.asyncio -async def test_get_state_snapshot_exposes_skipped_updates_total(): - r = _make_router() - r._skipped_updates_total = 7 - snap = await r.get_state_snapshot() - assert snap["skipped_updates_total"] == 7 - - # ---- endpoint -------------------------------------------------------- diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index e1133620a57..da02b774e41 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -948,6 +948,60 @@ class TestRouterComplexityDeploymentMethods: router.init_complexity_router_deployment(deployment) assert "auto_router/complexity_router/test-router" in router.complexity_routers + def test_hybrid_initialization_waits_for_later_pool_deployments(self): + router = Router( + model_list=[ + { + "model_name": "hybrid", + "litellm_params": { + "model": "auto_router/complexity_router", + "complexity_router_default_model": "cheap", + "complexity_router_config": { + "adaptive": True, + "tiers": { + "SIMPLE": ["cheap"], + "MEDIUM": ["cheap", "premium"], + }, + }, + }, + }, + { + "model_name": "cheap", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "input_cost_per_token": 0.00000015, + }, + "model_info": { + "adaptive_router_preferences": { + "quality_tier": 1, + "strengths": [], + } + }, + }, + { + "model_name": "premium", + "litellm_params": { + "model": "openai/gpt-4o", + "input_cost_per_token": 0.000005, + }, + "model_info": { + "adaptive_router_preferences": { + "quality_tier": 3, + "strengths": [], + } + }, + }, + ] + ) + + adaptive = router.adaptive_routers["hybrid"] + assert adaptive.model_to_cost == { + "cheap": pytest.approx(0.00000015), + "premium": pytest.approx(0.000005), + } + assert adaptive.model_to_prefs["cheap"].quality_tier == 1 + assert adaptive.model_to_prefs["premium"].quality_tier == 3 + class TestAsyncPreRoutingHookMultiFormat: """Test async_pre_routing_hook with multiple input formats.""" @@ -1356,6 +1410,240 @@ class TestLLMClassifier: assert call_kwargs["metadata"] == request_metadata +class TestAdaptiveSoftFloors: + def test_adaptive_defaults_use_cost_weighted_cold_policy(self): + config = ComplexityRouterConfig( + adaptive=True, + tiers={"SIMPLE": ["cheap"]}, + ) + assert config.adaptive_weights.quality == pytest.approx(0.3) + assert config.adaptive_weights.cost == pytest.approx(0.7) + assert config.tier_distance_penalty == pytest.approx(0.5) + + @pytest.fixture + def adaptive_router_instance(self): + router = MagicMock() + router.model_list = [ + { + "model_name": "cheap", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "input_cost_per_token": 0.00000015, + }, + "model_info": { + "adaptive_router_preferences": {"quality_tier": 1, "strengths": []} + }, + }, + { + "model_name": "premium", + "litellm_params": { + "model": "openai/gpt-4o", + "input_cost_per_token": 0.000005, + }, + "model_info": { + "adaptive_router_preferences": {"quality_tier": 3, "strengths": []} + }, + }, + ] + router.model_name_to_deployment_indices = {"cheap": [0], "premium": [1]} + return router + + @pytest.fixture + def hybrid_config(self) -> Dict: + return { + "adaptive": True, + "adaptive_weights": {"quality": 0.7, "cost": 0.3}, + "tier_distance_penalty": 0.15, + "tiers": { + "SIMPLE": ["cheap"], + "MEDIUM": ["cheap"], + "COMPLEX": ["premium"], + "REASONING": ["premium"], + }, + "default_model": "cheap", + } + + def test_adaptive_config_requires_non_empty_pools(self): + with pytest.raises(ValidationError): + ComplexityRouterConfig(adaptive=True, tiers={"SIMPLE": []}) + + def test_cold_start_randomly_samples_unobserved_classified_tier_models( + self, adaptive_router_instance + ): + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config={ + "adaptive": True, + "tiers": { + "SIMPLE": ["cheap", "premium"], + "MEDIUM": ["premium"], + }, + }, + ) + request_kwargs: Dict = {"metadata": {}} + + with patch( + "litellm.router_strategy.complexity_router.complexity_router.random.choice", + return_value="premium", + ) as choice: + picked = cr._soft_floor_pick(ComplexityTier.SIMPLE, "hi", request_kwargs) + + assert picked == "premium" + choice.assert_called_once_with(("cheap", "premium")) + decision = request_kwargs["metadata"]["adaptive_router_decision"] + assert decision["phase"] == "cold_start" + assert {candidate["model"] for candidate in decision["candidates"]} == { + "cheap", + "premium", + } + + def test_get_model_for_tier_list_without_adaptive_random_choice( + self, mock_router_instance + ): + router = ComplexityRouter( + model_name="test", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "adaptive": False, + "tiers": {"SIMPLE": ["cheap", "premium"], "MEDIUM": "mid"}, + "default_model": "mid", + }, + ) + pool = ["cheap", "premium"] + with patch( + "litellm.router_strategy.complexity_router.complexity_router.random.choice", + return_value="premium", + ) as choice: + assert router.get_model_for_tier(ComplexityTier.SIMPLE) == "premium" + choice.assert_called_once_with(pool) + assert router.get_model_for_tier(ComplexityTier.MEDIUM) == "mid" + + def test_soft_floor_prefers_home_tier_when_posteriors_equal( + self, adaptive_router_instance, hybrid_config + ): + from litellm.router_strategy.adaptive_router.bandit import BanditCell + from litellm.types.router import RequestType + + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config=hybrid_config, + ) + adaptive = cr._ensure_adaptive_router() + assert adaptive is not None + for model in ("cheap", "premium"): + adaptive._cells[(RequestType.GENERAL, model)] = BanditCell( + alpha=5.0, beta=5.0 + ) + + # Equal quality samples; home-tier penalty should favor cheap for SIMPLE. + with patch( + "litellm.router_strategy.adaptive_router.bandit.thompson_sample", + return_value=0.5, + ): + picked = cr._soft_floor_pick(ComplexityTier.SIMPLE, "hi") + assert picked == "cheap" + + def test_soft_floor_allows_cross_tier_when_posterior_dominates( + self, adaptive_router_instance, hybrid_config + ): + from litellm.router_strategy.adaptive_router.bandit import BanditCell + from litellm.types.router import RequestType + + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config=hybrid_config, + ) + adaptive = cr._ensure_adaptive_router() + assert adaptive is not None + adaptive._cells[(RequestType.GENERAL, "cheap")] = BanditCell( + alpha=1.0, beta=20.0 + ) + adaptive._cells[(RequestType.GENERAL, "premium")] = BanditCell( + alpha=20.0, beta=1.0 + ) + + with patch( + "litellm.router_strategy.adaptive_router.bandit.thompson_sample", + side_effect=lambda cell, rng=None: cell.alpha / (cell.alpha + cell.beta), + ): + picked = cr._soft_floor_pick(ComplexityTier.SIMPLE, "hi") + assert picked == "premium" + + def test_reused_model_has_zero_distance_in_each_configured_tier( + self, adaptive_router_instance + ): + from litellm.router_strategy.adaptive_router.bandit import BanditCell + from litellm.types.router import RequestType + + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config={ + "adaptive": True, + "tiers": { + "SIMPLE": ["cheap"], + "MEDIUM": ["cheap", "premium"], + "COMPLEX": ["premium"], + }, + }, + ) + adaptive = cr._ensure_adaptive_router() + assert adaptive is not None + for model in ("cheap", "premium"): + adaptive._cells[(RequestType.GENERAL, model)] = BanditCell( + alpha=6.0, beta=5.0 + ) + request_kwargs: Dict = {"metadata": {}} + + with patch( + "litellm.router_strategy.adaptive_router.bandit.thompson_sample", + return_value=0.5, + ): + cr._soft_floor_pick(ComplexityTier.MEDIUM, "hi", request_kwargs) + + candidates = request_kwargs["metadata"]["adaptive_router_decision"][ + "candidates" + ] + assert { + candidate["model"]: candidate["tier_distance"] for candidate in candidates + } == { + "cheap": 0, + "premium": 0, + } + + @pytest.mark.asyncio + async def test_pre_routing_hook_adaptive_stashes_chosen_model( + self, adaptive_router_instance, hybrid_config + ): + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config=hybrid_config, + ) + request_kwargs: Dict = {"metadata": {}} + result = await cr.async_pre_routing_hook( + model="hybrid", + request_kwargs=request_kwargs, + messages=[{"role": "user", "content": "hi"}], + ) + assert result is not None + assert result.model in {"cheap", "premium"} + assert ( + request_kwargs["metadata"].get("adaptive_router_chosen_model") + == result.model + ) + decision = request_kwargs["metadata"]["adaptive_router_decision"] + assert decision["phase"] == "cold_start" + assert decision["classified_tier"] == "SIMPLE" + assert decision["request_type"] == "general" + assert decision["eligible_mode"] == "classified_tier" + assert decision["chosen_model"] == result.model + assert {candidate["model"] for candidate in decision["candidates"]} == {"cheap"} + + class TestLexicalKeywordTierRules: """Test deterministic (literal) keyword_tier_rules overrides.""" diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index 50168056eaa..c31ee41a6ec 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -111,7 +111,7 @@ const ComplexityRouterConfig: React.FC = ({ classifier_type: classifierType, classifier_llm_config: classifierType === "llm" - ? value.classifier_llm_config ?? { model: "", timeout_ms: DEFAULT_CLASSIFIER_TIMEOUT_MS } + ? (value.classifier_llm_config ?? { model: "", timeout_ms: DEFAULT_CLASSIFIER_TIMEOUT_MS }) : undefined, }); }; diff --git a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts index 3eddca8c35b..a4a8ee6b074 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts @@ -43,8 +43,7 @@ export const getSemanticConfigError = ({ embeddingModel, keywordTierRules, }: Pick): - | string - | null => { + string | null => { if (!semanticMatchingEnabled) return null; if (!embeddingModel) return "Select an embedding model to use semantic keyword matching"; if (keywordTierRules.length === 0) return "Add at least one keyword tier rule to use semantic keyword matching"; From 3a2d14e1a6f19d7b0cc30169f8b5260aa642b73c Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Mon, 13 Jul 2026 11:33:02 +1000 Subject: [PATCH 313/399] fix(responses): continue MCP gateway tool turns from the final response and surface failures When a /responses request uses a hosted MCP tool (server_url: litellm_proxy/
)} +
+ Model Aliases + {(() => { + const aliasEntries = Object.entries(info.litellm_model_table?.model_aliases ?? {}); + if (aliasEntries.length === 0) { + return
No model aliases configured
; + } + return ( +
+ {aliasEntries.map(([alias, target]) => ( +
+ {alias} + {" -> "} + {target} +
+ ))} +
+ ); + })()} +
Rate Limits
TPM: {info.tpm_limit || "Unlimited"}
From 20e646c49a6c3ef5ce5c6957b807fa52d0fa7fe3 Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Mon, 13 Jul 2026 10:20:07 -0700 Subject: [PATCH 318/399] fix(ci): bump pillow to 12.3.0 to resolve osv-scan CVEs (#33093) --- pyproject.toml | 2 +- uv.lock | 117 ++++++++++++++++++++----------------------------- 2 files changed, 49 insertions(+), 70 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c2a0e85d13a..2c796d14c16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -205,7 +205,7 @@ ci = [ # protobuf, Pillow is a compiled C extension). "tenacity==8.5.0", "google-generativeai==0.8.6", - "Pillow==12.2.0", + "Pillow==12.3.0", # Azure batch E2E tests still import psycopg2 directly. "psycopg2-binary==2.9.11", "pytest-codspeed==4.3.0", diff --git a/uv.lock b/uv.lock index 00db09ef4ec..b120547c536 100644 --- a/uv.lock +++ b/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-07-08T23:20:11.959202Z" +exclude-newer = "2026-07-10T16:47:58.286372Z" exclude-newer-span = "P3D" [manifest] @@ -3588,7 +3588,7 @@ ci = [ { name = "logfire", specifier = "==4.6.0" }, { name = "lunary", marker = "python_full_version == '3.10.*'", specifier = "==1.4.36" }, { name = "lunary", marker = "python_full_version >= '3.11'", specifier = "==1.4.37" }, - { name = "pillow", specifier = "==12.2.0" }, + { name = "pillow", specifier = "==12.3.0" }, { name = "psycopg2-binary", specifier = "==2.9.11" }, { name = "pyarrow", specifier = "==23.0.1" }, { name = "pygithub", specifier = "==2.8.1" }, @@ -5279,75 +5279,54 @@ wheels = [ [[package]] name = "pillow" -version = "12.2.0" +version = "12.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, - { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, - { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, - { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, - { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, - { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, - { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, - { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, - { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, - { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, - { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, - { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, - { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, - { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, - { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, - { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, + { url = "https://files.pythonhosted.org/packages/25/c2/669d88644cddb1485bd9534e63e8cf476c8e51cb3c3a1297677023505c0e/pillow-12.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a", size = 5392418, upload-time = "2026-07-01T11:53:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ba/3762f376a2948e3036488d773a146e0ae6ecc2ca03ac20e2615bd0b2ba02/pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7", size = 4785287, upload-time = "2026-07-01T11:53:29.761Z" }, + { url = "https://files.pythonhosted.org/packages/07/50/b5d688cc9c52d4482f3d5bcab6ce20bc2a74a85d2343841c907444a3be2c/pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f", size = 6253754, upload-time = "2026-07-01T11:53:32.298Z" }, + { url = "https://files.pythonhosted.org/packages/4e/89/36f4cd76cf4baf05c50ababb976249153f18c959171c7f6ba09a6f217260/pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec", size = 6925605, upload-time = "2026-07-01T11:53:34.487Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c0/4de58cf6633b9e3a6061ef4be6fb91fc3c90b812ece886f531e3c523d777/pillow-12.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468", size = 6327788, upload-time = "2026-07-01T11:53:36.433Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/14d53682a19550dbbaf3b598f807d5457646c510805a44c7d7891cd1cd1a/pillow-12.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed", size = 7036288, upload-time = "2026-07-01T11:53:38.712Z" }, + { url = "https://files.pythonhosted.org/packages/38/1d/36279e3c77efe034e4cc2b0393ee74ffdb5a62391dacbf9b916154f5f0b8/pillow-12.3.0-cp310-cp310-win32.whl", hash = "sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1", size = 6472396, upload-time = "2026-07-01T11:53:40.781Z" }, + { url = "https://files.pythonhosted.org/packages/48/7c/8fa0039574c476d7c6fa57dd7c32a130436877c6ec1e5ce1cc8ec44878c1/pillow-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb", size = 7226887, upload-time = "2026-07-01T11:53:42.764Z" }, + { url = "https://files.pythonhosted.org/packages/fa/17/e324be141d173c1c919428066c3259f21c1b8982e564e01a4a81e96dbdcf/pillow-12.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f", size = 2568039, upload-time = "2026-07-01T11:53:45.372Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c8/0a78b0e02d7ac54bc03e5321c9220da52f0c2ea83b21f7c40e7f3169c502/pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756", size = 5392415, upload-time = "2026-07-01T11:53:47.162Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5b/a02d30018abd97ced9f5a6c63d28597694a00d066516b9c1c6de45859fc9/pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6", size = 4785266, upload-time = "2026-07-01T11:53:49.079Z" }, + { url = "https://files.pythonhosted.org/packages/c8/98/766667a4be768150a202836acd9fad19c06824ca86c4286d3cf6b274964e/pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd", size = 6263814, upload-time = "2026-07-01T11:53:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2d/ede717bc1144f63886c21fd349bb95860b0d1a21149ff16f2bb362b612b6/pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd", size = 6934408, upload-time = "2026-07-01T11:53:53.487Z" }, + { url = "https://files.pythonhosted.org/packages/a3/48/9c58b685e69d49c31af6c8eb9012055fab7e665785165c84796e2c73ce72/pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c", size = 6337160, upload-time = "2026-07-01T11:53:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/ff/fa/dc2a5c0ba6df93f67c31d34b808b7ce440b40cdbf96f0b81cde1d1e6fa93/pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5", size = 7045172, upload-time = "2026-07-01T11:53:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/86/a5/444817a4d4c4c2417df00513086ca196f388d8f9ef40c2e4ccd1ad1af54b/pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b", size = 6472232, upload-time = "2026-07-01T11:53:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/63/c6/4bad1b18d132a50b27e1365e1ab163616f7a5bb56d330f66f9d1d9d4f9d4/pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a", size = 7233653, upload-time = "2026-07-01T11:54:02.066Z" }, + { url = "https://files.pythonhosted.org/packages/fd/16/00f91ab7760dc842f5aad55217e80fc4a7067a0604535249bc8a2d6d9870/pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26", size = 2568195, upload-time = "2026-07-01T11:54:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" }, + { url = "https://files.pythonhosted.org/packages/08/14/f6ba68107680ffa74b39985f3f30884e41318fbc4250caa423c79b4788bb/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3", size = 5860358, upload-time = "2026-07-01T11:56:32.68Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0169bc772ec491108b62f644f8ecf1fe5d8ae5ebafde2ee2142210166903/pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a", size = 7231786, upload-time = "2026-07-01T11:56:35.046Z" }, ] [[package]] From 8936d07be887f61ae10ba854ca8777b6d2e88d17 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:39:38 -0400 Subject: [PATCH 319/399] fix(proxy): track unauthenticated pass-through requests in spend logs (#32410) Pass-through endpoints configured with auth=false reach the cost-tracking callback with no key/user/team/end-user, so _should_track_cost_callback returned False and the spend-log write was skipped, leaving the request out of request/usage logs. Track pass-through call types even when unauthenticated so the SpendLog row is still written. Co-authored-by: Mubashir Osmani Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../proxy/hooks/proxy_track_cost_callback.py | 19 +++- .../hooks/test_proxy_track_cost_callback.py | 86 +++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/hooks/proxy_track_cost_callback.py b/litellm/proxy/hooks/proxy_track_cost_callback.py index b6342f4fa1a..b839426fcda 100644 --- a/litellm/proxy/hooks/proxy_track_cost_callback.py +++ b/litellm/proxy/hooks/proxy_track_cost_callback.py @@ -28,11 +28,20 @@ from litellm.proxy.spend_tracking.spend_tracking_utils import ( ) from litellm.proxy.utils import ProxyUpdateSpend from litellm.types.utils import ( + CallTypes, StandardLoggingPayload, StandardLoggingPayloadErrorInformation, ) from litellm.utils import get_end_user_id_for_cost_tracking +_PASS_THROUGH_CALL_TYPES: frozenset[str] = frozenset( + { + CallTypes.pass_through.value, + CallTypes.llm_passthrough_route.value, + CallTypes.allm_passthrough_route.value, + } +) + class _ProxyDBLogger(CustomLogger): async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): @@ -219,11 +228,13 @@ class _ProxyDBLogger(CustomLogger): verbose_proxy_logger.debug( f"user_api_key {user_api_key}, user_id {user_id}, team_id {team_id}, end_user_id {end_user_id}" ) + call_type: Optional[str] = kwargs.get("call_type") if _should_track_cost_callback( user_api_key=user_api_key, user_id=user_id, team_id=team_id, end_user_id=end_user_id, + call_type=call_type, ): ## UPDATE DATABASE await _update_database_and_spend_counters( @@ -412,9 +423,15 @@ def _should_track_cost_callback( user_id: Optional[str], team_id: Optional[str], end_user_id: Optional[str], + call_type: Optional[str] = None, ) -> bool: """ Determine if the cost callback should be tracked based on the kwargs + + Pass-through endpoints can be configured with ``auth=false``, which leaves + the request with no key/user/team/end-user to attribute spend to. Those + requests still forward real provider traffic that operators expect to see + in request/usage logs, so they are tracked even when unauthenticated. """ # don't run track cost callback if user opted into disabling spend @@ -423,7 +440,7 @@ def _should_track_cost_callback( if user_api_key is not None or user_id is not None or team_id is not None or end_user_id is not None: return True - return False + return call_type in _PASS_THROUGH_CALL_TYPES def _get_budget_reservation_from_metadata(metadata: dict) -> Optional[dict]: diff --git a/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py b/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py index 813a0c5e38f..f289148101a 100644 --- a/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py +++ b/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py @@ -14,6 +14,7 @@ from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.hooks.proxy_track_cost_callback import ( _ProxyDBLogger, _get_budget_reservation_from_metadata, + _should_track_cost_callback, _update_database_and_spend_counters, ) @@ -1177,3 +1178,88 @@ async def test_track_cost_callback_enriches_user_id_for_mcp_style_metadata(): kwargs["litellm_params"]["metadata"]["user_api_key_user_id"] == "mcp-user@example.com" ) + + +@pytest.mark.parametrize( + "call_type, expected", + [ + ("pass_through_endpoint", True), + ("llm_passthrough_route", True), + ("allm_passthrough_route", True), + ("acompletion", False), + ("call_mcp_tool", False), + (None, False), + ], +) +def test_should_track_cost_callback_pass_through_without_owner(call_type, expected): + """Regression for LIT-3782: unauthenticated pass-through requests (auth=false) + carry no key/user/team/end-user, yet must still be tracked so they land in + LiteLLM_SpendLogs. Other call types with no owner stay untracked.""" + assert ( + _should_track_cost_callback( + user_api_key=None, + user_id=None, + team_id=None, + end_user_id=None, + call_type=call_type, + ) + is expected + ) + + +@pytest.mark.parametrize( + "call_type, expect_spend_log", + [ + ("pass_through_endpoint", True), + ("acompletion", False), + (None, False), + ], +) +@pytest.mark.asyncio +async def test_track_cost_callback_logs_unauthenticated_pass_through_request( + call_type, expect_spend_log +): + """Regression for LIT-3782: a pass-through request with auth=false reaches the + cost callback with no key/user/team/end-user. Before the fix the spend-log + write was skipped and the request never appeared in request/usage logs. It + must now be written for pass-through call types while other unauthenticated + calls remain skipped.""" + logger = _ProxyDBLogger() + + kwargs = { + "call_type": call_type, + "model": "unknown", + "litellm_params": {"metadata": {}}, + "standard_logging_object": { + "response_cost": 0.0, + "request_tags": None, + }, + "stream": False, + } + + with ( + patch( + "litellm.proxy.proxy_server.increment_spend_counters", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy.proxy_server.update_cache", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy.proxy_server.proxy_logging_obj", + ) as mock_proxy_logging, + ): + mock_proxy_logging.db_spend_update_writer.update_database = AsyncMock() + mock_proxy_logging.slack_alerting_instance.customer_spend_alert = AsyncMock() + + await logger._PROXY_track_cost_callback( + kwargs=kwargs, + completion_response=None, + start_time=datetime.now(), + end_time=datetime.now(), + ) + + assert mock_proxy_logging.db_spend_update_writer.update_database.await_count == ( + 1 if expect_spend_log else 0 + ) From 78e5c4330124622bf8293d3a35498c8c37ff2b24 Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Mon, 13 Jul 2026 10:39:41 -0700 Subject: [PATCH 320/399] feat(lasso): send source.type=litellm for Used By attribution (#33090) Co-authored-by: Or Gershoni --- litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py | 8 +++++++- .../proxy/guardrails/guardrail_hooks/test_lasso.py | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py b/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py index 9c4cef2f06b..31ce0cc2214 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py +++ b/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py @@ -772,7 +772,13 @@ class LassoGuardrail(CustomGuardrail): data: Request data (used for conversation_id generation and tools extraction) cache: Cache instance for storing conversation_id (optional for post-call) """ - payload: Dict[str, Any] = {"messages": messages, "messageType": message_type} + payload: Dict[str, Any] = { + "messages": messages, + "messageType": message_type, + # Drives the "Used By" badge on Lasso Application API Keys: every call from this + # integration is attributed as "litellm" on the keys list. + "source": {"type": "litellm"}, + } # Add optional parameters if available if self.user_id: diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_lasso.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_lasso.py index 5a84b6ebecd..16185cadbdf 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_lasso.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_lasso.py @@ -693,6 +693,8 @@ class TestLassoGuardrail: assert prompt_payload["messages"] == messages assert prompt_payload["userId"] == "test-user" assert prompt_payload["sessionId"] == "test-conversation" + # Every call is attributed to the "litellm" integration for the "Used By" badge. + assert prompt_payload["source"] == {"type": "litellm"} # Test COMPLETION payload completion_messages = [{"role": "assistant", "content": "Test response"}] @@ -703,6 +705,7 @@ class TestLassoGuardrail: assert completion_payload["messages"] == completion_messages assert completion_payload["userId"] == "test-user" assert completion_payload["sessionId"] == "test-conversation" + assert completion_payload["source"] == {"type": "litellm"} def test_header_preparation(self): """Test header preparation.""" From 45fed6a50a231822aeb616c99d4b6f17ffd48da0 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 14:44:10 -0700 Subject: [PATCH 321/399] feat(mcp): generalize the bridge envelope identity to a key_hash or user_id subject The scripted two-header client mints under a virtual key it presents at the token endpoint (key_hash), but the interactive DCR client authenticates via SSO at the bridged authorize, which yields a user, not a key. Make EnvelopeIdentity a discriminated subject (subject_type key_hash | user_id) with key_hash_identity / user_identity constructors, and dispatch admission on it: a key_hash reloads the key, a user_id reloads the user and admits them as themselves (user-level budget and SCIM enforced via the same centralized gate; no team bound, since a user belongs to many teams or none). The interactive producer that mints a user_id envelope lands in the follow-up commit. --- .../mcp_server/auth/user_api_key_auth_mcp.py | 57 +++++++++- .../mcp_server/discoverable_endpoints.py | 4 +- .../outbound_credentials/envelope.py | 48 ++++++-- .../auth/test_user_api_key_auth_mcp.py | 105 +++++++++++++++++- .../test_bridge_credentials.py | 7 +- .../outbound_credentials/test_envelope.py | 35 ++++-- .../mcp_server/test_discoverable_endpoints.py | 3 +- 7 files changed, 230 insertions(+), 29 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index e300a22e5db..faec35db41a 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -18,6 +18,9 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credenti is_bridge_envelope_shaped, resolve_bridge_envelope, ) +from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + EnvelopeIdentity, +) from litellm.proxy._types import ( UI_TEAM_ID, LiteLLM_TeamTable, @@ -543,7 +546,7 @@ class MCPRequestHandler: header_key = server.alias or server.server_name if header_key is None: raise HTTPException(status_code=500, detail="Server misconfigured: MCP server has no routable name") - admitted = await MCPRequestHandler._reload_admitted_key(result.identity.key_hash) + admitted = await MCPRequestHandler._reload_admitted_principal(result.identity) await MCPRequestHandler._enforce_admitted_live_policy(admitted=admitted, request=request, route=route) injected = {header_key: {"Authorization": result.upstream_authorization.get_secret_value()}} new_headers = {**(mcp_server_auth_headers or {}), **injected} @@ -572,6 +575,58 @@ class MCPRequestHandler: route=route, ) + @staticmethod + async def _reload_admitted_principal(identity: EnvelopeIdentity) -> UserAPIKeyAuth: + """Reload the live litellm record the envelope's subject references. + + Dispatches on the sealed subject type: a ``key_hash`` reloads the virtual key that + minted the envelope (the scripted two-header client that presents a litellm key at the + token endpoint), a ``user_id`` reloads the user that authenticated interactively (the + DCR client, whose SSO login at the bridged authorize yields a user, not a key). Both + return a ``UserAPIKeyAuth`` the caller runs through the centralized policy gate, so + team/project/org/budget/SCIM enforcement is identical to the principal presenting + itself directly.""" + match identity.subject_type: + case "key_hash": + return await MCPRequestHandler._reload_admitted_key(identity.subject) + case "user_id": + return await MCPRequestHandler._reload_admitted_user(identity.subject) + case _: + assert_never(identity.subject_type) + + @staticmethod + async def _reload_admitted_user(user_id: str) -> UserAPIKeyAuth: + """Reload the live user an interactively-minted envelope references and admit them as + themselves. + + The DCR client authenticates via SSO at the bridged authorize, which yields a user + subject rather than a virtual key, so the envelope admits under the user's own + identity: the reloaded ``user_id`` rides on the returned ``UserAPIKeyAuth`` and the + caller's centralized policy gate then enforces the user's live budget and org state, + and a SCIM-deactivated owner fails closed here exactly as the key path enforces it. No + team is bound; a user may belong to many teams or none, so the envelope grants the + user's own access rather than silently selecting one team's scope. A missing user + fails closed with a 401 rather than admitting an unresolved identity.""" + from litellm.proxy.auth.auth_checks import get_user_object + from litellm.proxy.proxy_server import prisma_client, user_api_key_cache + + if prisma_client is None: + raise HTTPException(status_code=500, detail="Server misconfigured: no database connection") + try: + user_object = await get_user_object( + user_id=user_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + user_id_upsert=False, + ) + except (ProxyException, HTTPException): + raise HTTPException(status_code=401, detail="Invalid or expired credential") from None + if user_object is None: + raise HTTPException(status_code=401, detail="Invalid or expired credential") + if isinstance(user_object.metadata, dict) and user_object.metadata.get("scim_active") is False: + raise HTTPException(status_code=401, detail="Invalid or expired credential") + return UserAPIKeyAuth(user_id=user_object.user_id) + @staticmethod async def _reload_admitted_key(key_hash: str) -> UserAPIKeyAuth: """Reload the live key record an admitted envelope references and re-check live policy. diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 8d1713a5911..3e727ce95bb 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -961,15 +961,15 @@ def _finish_bridge_mint( build_bridge_token_response, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import - EnvelopeIdentity, SealedEnvelope, UpstreamTokenGrant, + key_hash_identity, ) grant = _bridge_grant_from_token_response(token_response) if not isinstance(grant, UpstreamTokenGrant): return _upstream_rejection_to_mint_error(grant) - identity = EnvelopeIdentity(server_id=mcp_server.server_id, key_hash=ready.key_hash) + identity = key_hash_identity(server_id=mcp_server.server_id, key_hash=ready.key_hash) sealed = build_bridge_token_response(identity, grant, ready.keys, now) if not isinstance(sealed, SealedEnvelope): return "too_large" diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py index 517c2ef5c8f..783e64d13e2 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py @@ -67,20 +67,42 @@ typed error, never truncated.""" _ENVELOPE_JWT_ALGORITHM = "HS256" -class EnvelopeIdentity(BaseModel): - """The litellm identity the envelope binds the inner grant to. +EnvelopeSubjectType: TypeAlias = Literal["key_hash", "user_id"] +"""Discriminator for what litellm principal the envelope binds the grant to. - ``key_hash`` is the hashed litellm key that authorized the mint, never a raw - credential (and the edge rejects a bare hash presented as a bearer). Admission - reloads the live key record by it, so the key's current team/org/object-permission - restrictions and its revocation state are enforced at use time rather than frozen at - mint time. ``server_id`` binds the envelope to one MCP server so it cannot be replayed - across a server boundary. +``key_hash`` is a hashed virtual key (the scripted two-header client mints under the key it +presents at the token endpoint); ``user_id`` is a litellm user subject (the interactive DCR +client mints under the SSO-authenticated user, which is the only identity that browser login +yields). Admission reloads a key record for the first and a user record for the second, then +runs both through the same live-policy gate, so team/org/budget/revocation enforcement is +identical either way.""" + + +class EnvelopeIdentity(BaseModel): + """The litellm principal the envelope binds the inner grant to. + + ``subject`` is the principal identifier and ``subject_type`` says how to resolve it: a + hashed litellm key (``key_hash``) or a litellm user id (``user_id``), never a raw + credential (and the edge rejects a bare hash or id presented as a bearer). Admission + reloads the live record by it, so the principal's current team/org restrictions and its + revocation state are enforced at use time rather than frozen at mint time. ``server_id`` + binds the envelope to one MCP server so it cannot be replayed across a server boundary. """ model_config = ConfigDict(frozen=True) server_id: str = Field(min_length=1) - key_hash: str = Field(min_length=1) + subject_type: EnvelopeSubjectType + subject: str = Field(min_length=1) + + +def key_hash_identity(server_id: str, key_hash: str) -> EnvelopeIdentity: + """The identity for the scripted client that mints under a presented virtual key.""" + return EnvelopeIdentity(server_id=server_id, subject_type="key_hash", subject=key_hash) + + +def user_identity(server_id: str, user_id: str) -> EnvelopeIdentity: + """The identity for the interactive DCR client that mints under its SSO user subject.""" + return EnvelopeIdentity(server_id=server_id, subject_type="user_id", subject=user_id) class UpstreamTokenGrant(BaseModel): @@ -200,7 +222,8 @@ class _EnvelopeClaims(BaseModel): iat: int exp: int server_id: str = Field(min_length=1) - key_hash: str = Field(min_length=1) + subject_type: EnvelopeSubjectType + subject: str = Field(min_length=1) grant: str = Field(min_length=1) @@ -236,7 +259,8 @@ def mint_envelope( iat=int(now.timestamp()), exp=int(expires_at.timestamp()), server_id=identity.server_id, - key_hash=identity.key_hash, + subject_type=identity.subject_type, + subject=identity.subject, grant=_encrypt_grant_blob(_grant_plaintext(grant), keys.encryption_key), ) token = ENVELOPE_PREFIX + jwt.encode( @@ -281,7 +305,7 @@ def open_envelope( if not isinstance(grant, UpstreamTokenGrant): return grant return OpenedEnvelope( - identity=EnvelopeIdentity(server_id=claims.server_id, key_hash=claims.key_hash), + identity=EnvelopeIdentity(server_id=claims.server_id, subject_type=claims.subject_type, subject=claims.subject), grant=grant, ) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index c785ac577f7..a6affe5496c 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -4910,6 +4910,7 @@ class TestMCPDcrBridgeDelegateAdmission: cls, *, key_hash=None, + user_id=None, server_id="bridge-server-id", access_token="inner-upstream-access-token", token_type="Bearer", @@ -4921,17 +4922,23 @@ class TestMCPDcrBridgeDelegateAdmission: envelope_keys_from_master_key, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( - EnvelopeIdentity, SealedEnvelope, UpstreamTokenGrant, + key_hash_identity, mint_envelope, + user_identity, ) from pydantic import SecretStr + identity = ( + user_identity(server_id=server_id, user_id=user_id) + if user_id is not None + else key_hash_identity(server_id=server_id, key_hash=key_hash or cls._KEY_HASH) + ) keys = envelope_keys_from_master_key(master_key or cls._MASTER_KEY) now = minted_at or datetime.now(timezone.utc) sealed = mint_envelope( - identity=EnvelopeIdentity(server_id=server_id, key_hash=key_hash or cls._KEY_HASH), + identity=identity, grant=UpstreamTokenGrant( access_token=SecretStr(access_token), token_type=token_type, @@ -4999,6 +5006,22 @@ class TestMCPDcrBridgeDelegateAdmission: stack.enter_context(patcher) yield get_key_object + @staticmethod + @contextlib.contextmanager + def _patch_user_reload(*, return_value=None, side_effect=None): + """Patch the user-subject reload path an interactively-minted envelope takes: the + ``get_user_object`` lookup ``_reload_admitted_user`` runs (which also drives the SCIM gate), + plus the ``prisma_client`` / ``user_api_key_cache`` globals. The centralized gate's own + fetches fail-safe to None under the MagicMock prisma, so an unblocked user admits. Yields the + ``get_user_object`` mock so a caller can assert the sealed user_id was the reload key.""" + get_user_object = AsyncMock(return_value=return_value, side_effect=side_effect) + with ( + patch("litellm.proxy.auth.auth_checks.get_user_object", get_user_object), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + ): + yield get_user_object + @staticmethod def _mcp_request(path="/mcp/bridge_delegate_server"): """A minimal ``Request`` for direct ``_admit_dcr_bridge_delegate`` calls, mirroring how @@ -5060,6 +5083,84 @@ class TestMCPDcrBridgeDelegateAdmission: "bridge_delegate_server": {"Authorization": "Bearer inner-upstream-access-token"} } + async def test_user_subject_envelope_admits_under_the_reloaded_user(self): + """An interactively-minted (user_id) envelope admits under the reloaded USER, not a key: the + reload is keyed by the sealed user_id, the admitted auth carries that user_id, the raw-key + pipeline is never invoked, and the inner upstream token is injected for egress. This is the + interactive-DCR admission the whole flow exists for.""" + envelope = self._mint_bridge_envelope(user_id="sso-user-7") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_user_reload( + return_value=MagicMock(user_id="sso-user-7", metadata={"scim_active": True}) + ) as get_user_object, + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + (auth_result, _h, _s, mcp_server_auth_headers, _o, _r) = await MCPRequestHandler.process_mcp_request(scope) + + assert get_user_object.await_args.kwargs["user_id"] == "sso-user-7" + assert auth_result.user_id == "sso-user-7" + mock_auth.assert_not_called() + assert mcp_server_auth_headers == { + "bridge_delegate_server": {"Authorization": "Bearer inner-upstream-access-token"} + } + + async def test_user_subject_envelope_missing_user_fails_closed_401(self): + """A user_id envelope whose user has since been deleted must fail closed: get_user_object + resolves None, so admission 401s instead of admitting an unresolved identity.""" + envelope = self._mint_bridge_envelope(user_id="ghost-user") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_user_reload(return_value=None), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + + async def test_user_subject_envelope_scim_deactivated_user_fails_closed_401(self): + """SCIM-deactivating the envelope's user revokes it immediately: the reloaded user carries + scim_active False, so admission 401s rather than letting an offboarded user keep tool access + until the envelope expires.""" + envelope = self._mint_bridge_envelope(user_id="offboarded-user") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_user_reload( + return_value=MagicMock(user_id="offboarded-user", metadata={"scim_active": False}) + ), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + async def test_revoked_key_envelope_fails_closed_401(self): """An envelope whose key has since been deleted must fail closed: ``get_key_object`` raises for the missing row, so admission 401s instead of admitting the caller as an unrestricted diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py index 82e8e2aae89..ecea86bbed4 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py @@ -28,13 +28,14 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import EnvelopeTooLarge, SealedEnvelope, UpstreamTokenGrant, + key_hash_identity, mint_envelope, ) _NOW = datetime(2026, 7, 9, 12, 0, 0, tzinfo=timezone.utc) _MASTER_KEY = "sk-master-key-for-derivation-tests-0123456789" _ACCESS_TOKEN = "upstream-access-token-do-not-leak-8f14e45fceea" -_IDENTITY = EnvelopeIdentity(server_id="srv-456", key_hash="hashed-key-123") +_IDENTITY = key_hash_identity(server_id="srv-456", key_hash="hashed-key-123") _SERVER_ID = _IDENTITY.server_id @@ -138,7 +139,7 @@ def test_resolve_envelope_minted_for_another_server_is_invalid(): captured or misrouted envelope cannot forward one server's upstream credential to another. The valid access token stays sealed; the mismatch alone fails the resolve.""" keys = envelope_keys_from_master_key(_MASTER_KEY) - other_server_identity = EnvelopeIdentity(server_id="srv-OTHER", key_hash=_IDENTITY.key_hash) + other_server_identity = key_hash_identity(server_id="srv-OTHER", key_hash=_IDENTITY.subject) token = _sealed_token(keys, identity=other_server_identity) result = resolve_bridge_envelope(token, keys, _NOW, _SERVER_ID) assert isinstance(result, BridgeEnvelopeInvalid) @@ -155,7 +156,7 @@ def test_resolve_non_ascii_server_id_stays_total_and_does_not_raise(): unicode server_id); it stays total and returns a typed result. A matching non-ASCII id admits, a mismatching one is BridgeEnvelopeInvalid, and neither raises.""" keys = envelope_keys_from_master_key(_MASTER_KEY) - unicode_identity = EnvelopeIdentity(server_id="srv-café", key_hash=_IDENTITY.key_hash) + unicode_identity = key_hash_identity(server_id="srv-café", key_hash=_IDENTITY.subject) token = _sealed_token(keys, identity=unicode_identity) assert isinstance(resolve_bridge_envelope(token, keys, _NOW, "srv-café"), BridgeEnvelopeAdmitted) assert isinstance(resolve_bridge_envelope(token, keys, _NOW, "srv-cafe"), BridgeEnvelopeInvalid) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py index b44f3f84cc9..7a2b51c2a95 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py @@ -36,8 +36,10 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import SealedEnvelope, UpstreamTokenGrant, is_envelope, + key_hash_identity, mint_envelope, open_envelope, + user_identity, ) from litellm.proxy.common_utils.encrypt_decrypt_utils import decrypt_value, encrypt_value @@ -51,7 +53,7 @@ _WRONG_SIGNING = EnvelopeKeys(signing_key=SecretStr(_OTHER_SIGNING_KEY), encrypt _WRONG_ENCRYPTION = EnvelopeKeys(signing_key=SecretStr(_SIGNING_KEY), encryption_key=SecretStr(_OTHER_ENCRYPTION_KEY)) _ACCESS_TOKEN = "upstream-access-token-do-not-leak-8f14e45fceea" _REFRESH_TOKEN = "upstream-refresh-token-do-not-leak-1d0aa4b7" -_IDENTITY = EnvelopeIdentity(server_id="srv-456", key_hash="hashed-key-123") +_IDENTITY = key_hash_identity(server_id="srv-456", key_hash="hashed-key-123") def _full_grant() -> UpstreamTokenGrant: @@ -137,12 +139,13 @@ def test_minimal_grant_round_trips_without_none_leakage_into_claims(): def test_claim_layout_and_no_plaintext_token_in_envelope(): token = _sealed_token(_full_grant()) claims = _unverified_claims(token) - assert set(claims) == {"iss", "iat", "exp", "server_id", "key_hash", "grant"} + assert set(claims) == {"iss", "iat", "exp", "server_id", "subject_type", "subject", "grant"} assert claims["iss"] == ENVELOPE_ISSUER assert claims["iat"] == int(_NOW.timestamp()) assert claims["exp"] == int(_NOW.timestamp()) + 600 assert claims["server_id"] == "srv-456" - assert claims["key_hash"] == "hashed-key-123" + assert claims["subject_type"] == "key_hash" + assert claims["subject"] == "hashed-key-123" assert _ACCESS_TOKEN not in token assert _ACCESS_TOKEN not in json.dumps(claims) assert _REFRESH_TOKEN not in json.dumps(claims) @@ -226,11 +229,11 @@ def test_wrong_issuer_is_malformed_payload(): def test_missing_identity_claim_is_malformed_payload(): claims = _unverified_claims(_sealed_token(_full_grant())) - forged = _forge({key: value for key, value in claims.items() if key != "key_hash"}) + forged = _forge({key: value for key, value in claims.items() if key != "subject"}) assert isinstance(open_envelope(forged, _KEYS, _NOW), MalformedPayload) -@pytest.mark.parametrize("identity_claim", ["server_id", "key_hash"]) +@pytest.mark.parametrize("identity_claim", ["server_id", "subject"]) def test_signed_empty_identity_claim_is_malformed_payload_not_a_raise(identity_claim): claims = _unverified_claims(_sealed_token(_full_grant())) forged = _forge({**claims, identity_claim: ""}) @@ -463,9 +466,11 @@ def test_non_positive_expires_in_is_rejected_at_construction_without_leaking(): def test_empty_identity_and_key_fields_are_rejected_at_construction(): with pytest.raises(ValidationError): - EnvelopeIdentity(server_id="", key_hash="hashed-key-123") + EnvelopeIdentity(server_id="", subject_type="key_hash", subject="hashed-key-123") with pytest.raises(ValidationError): - EnvelopeIdentity(server_id="srv-456", key_hash="") + EnvelopeIdentity(server_id="srv-456", subject_type="key_hash", subject="") + with pytest.raises(ValidationError): + EnvelopeIdentity(server_id="srv-456", subject_type="not-a-subject-type", subject="x") with pytest.raises(ValidationError): EnvelopeKeys(signing_key=SecretStr(""), encryption_key=SecretStr(_ENCRYPTION_KEY)) with pytest.raises(ValidationError): @@ -474,6 +479,20 @@ def test_empty_identity_and_key_fields_are_rejected_at_construction(): UpstreamTokenGrant(access_token=SecretStr(""), token_type="Bearer") +def test_user_subject_identity_round_trips(): + """The user_id subject variant seals and opens with its discriminator intact, so the edge can + tell an interactively-minted (user) envelope from a scripted (key_hash) one and reload the right + kind of record.""" + identity = user_identity(server_id="srv-456", user_id="user-42") + sealed = mint_envelope(identity, _full_grant(), _KEYS, _NOW) + assert isinstance(sealed, SealedEnvelope) + opened = open_envelope(sealed.token.get_secret_value(), _KEYS, _NOW) + assert isinstance(opened, OpenedEnvelope) + assert opened.identity.server_id == "srv-456" + assert opened.identity.subject_type == "user_id" + assert opened.identity.subject == "user-42" + + def test_public_models_are_frozen(): sealed = mint_envelope(_IDENTITY, _full_grant(), _KEYS, _NOW) assert isinstance(sealed, SealedEnvelope) @@ -484,4 +503,4 @@ def test_public_models_are_frozen(): with pytest.raises(ValidationError): opened.grant = _minimal_grant() with pytest.raises(ValidationError): - _IDENTITY.key_hash = "someone-elses-hash" + _IDENTITY.subject = "someone-elses-hash" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index 68466e624ec..e43312e400e 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4441,7 +4441,8 @@ async def test_oauth_delegate_bridge_token_exchange_mints_envelope_not_raw_token keys = envelope_keys_from_master_key(_BRIDGE_MASTER_KEY) opened = resolve_bridge_envelope(token, keys, datetime.now(timezone.utc), server.server_id) assert isinstance(opened, BridgeEnvelopeAdmitted) - assert opened.identity.key_hash == "hashed-litellm-key-77" + assert opened.identity.subject_type == "key_hash" + assert opened.identity.subject == "hashed-litellm-key-77" assert opened.upstream_authorization.get_secret_value() == "Bearer UPSTREAM-SECRET-TOKEN" From 02e9c5631a88d0bdb52d1d4ccb1de21e9c29bede Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Sat, 11 Jul 2026 14:58:52 -0700 Subject: [PATCH 322/399] feat(mcp): interactive SSO sign-in for dcr_bridge oauth_delegate DCR clients Completes the oauth_delegate bridge for real DCR clients (Claude Code, Claude Desktop), which send no litellm key and cannot use the scripted two-header path. On the short-circuit bridge arm the gateway now captures the SSO-authenticated litellm user from the browser session at /authorize and seals it into the OAuth state; at /callback it seals that user plus the upstream code into a gateway authorization code the client echoes back; at /token it recovers the user, exchanges the real upstream code, and mints a user-subject envelope. The user identity captured in the browser thus rides to the back-channel token call with nothing stored server-side, and admission opens the envelope under that user. The scripted key_hash path is unchanged (raw upstream code, key from the request); without a session the browser is sent through login first. --- .../mcp_server/discoverable_endpoints.py | 176 +++++++++++++-- .../mcp_server/test_discoverable_endpoints.py | 200 +++++++++++++++++- 2 files changed, 352 insertions(+), 24 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 3e727ce95bb..bd26abe0b26 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -12,7 +12,7 @@ from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse import httpx from fastapi import APIRouter, Form, HTTPException, Request from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response -from pydantic import BaseModel, SecretStr, ValidationError +from pydantic import BaseModel, ConfigDict, Field, SecretStr, ValidationError from typing_extensions import assert_never from litellm._logging import verbose_logger @@ -41,6 +41,7 @@ from litellm.types.mcp_server.mcp_server_manager import MCPServer if TYPE_CHECKING: from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + EnvelopeIdentity, EnvelopeKeys, UpstreamTokenGrant, ) @@ -98,6 +99,8 @@ def encode_state_with_base_url( code_challenge: Optional[str] = None, code_challenge_method: Optional[str] = None, client_redirect_uri: Optional[str] = None, + litellm_user_id: str | None = None, + mcp_server_id: str | None = None, ) -> str: """ Encode the base_url, original state, and PKCE parameters using encryption. @@ -108,6 +111,11 @@ def encode_state_with_base_url( code_challenge: PKCE code challenge from client code_challenge_method: PKCE code challenge method from client client_redirect_uri: Original redirect_uri from client + litellm_user_id: The SSO-authenticated litellm user captured at the bridge authorize + (interactive dcr_bridge oauth_delegate only); the callback seals it into the gateway + authorization code so the token mint can bind the envelope to this user + mcp_server_id: The bridge server the interactive flow targets, sealed alongside + litellm_user_id so the gateway code cannot be replayed against another server Returns: An encrypted string that encodes all values @@ -118,6 +126,8 @@ def encode_state_with_base_url( "code_challenge": code_challenge, "code_challenge_method": code_challenge_method, "client_redirect_uri": client_redirect_uri, + "litellm_user_id": litellm_user_id, + "mcp_server_id": mcp_server_id, } state_json = json.dumps(state_data, sort_keys=True) encrypted_state = encrypt_value_helper(state_json) @@ -145,6 +155,68 @@ def decode_state_hash(encrypted_state: str) -> dict: return state_data +_BRIDGE_AUTH_CODE_PREFIX = "llm_bcode_" + + +class _BridgeAuthorizationCode(BaseModel): + """The identity and upstream code the gateway seals into the authorization code it hands a DCR + client for an interactive dcr_bridge oauth_delegate sign-in, recovered at the token endpoint.""" + + model_config = ConfigDict(frozen=True) + upstream_code: str = Field(min_length=1) + litellm_user_id: str = Field(min_length=1) + mcp_server_id: str = Field(min_length=1) + + +def is_bridge_authorization_code(code: str) -> bool: + """Cheap prefix check that ``code`` is a gateway-sealed bridge authorization code rather than a + raw upstream code, so the token endpoint can route without decrypting.""" + return code.startswith(_BRIDGE_AUTH_CODE_PREFIX) + + +def seal_bridge_authorization_code(upstream_code: str, litellm_user_id: str, mcp_server_id: str) -> str: + """Seal the upstream authorization code and the SSO-captured litellm user into a gateway + authorization code. The DCR client only echoes this opaque value back at the token endpoint; the + gateway decrypts it there to recover the user (to bind the envelope) and the upstream code (to + exchange with the upstream), so a litellm identity captured in the browser at authorize survives + to the back-channel token call with nothing stored server-side. Encrypted with the repo's + authenticated symmetric helper (the same family the OAuth state uses), so the client can neither + read nor forge it.""" + payload = json.dumps( + {"upstream_code": upstream_code, "litellm_user_id": litellm_user_id, "mcp_server_id": mcp_server_id}, + sort_keys=True, + ) + return _BRIDGE_AUTH_CODE_PREFIX + encrypt_value_helper(payload) + + +def open_bridge_authorization_code(code: str) -> _BridgeAuthorizationCode | None: + """Recover the sealed identity and upstream code, or ``None`` when ``code`` is not a gateway + bridge code or does not decrypt / validate. Total over hostile input: a raw upstream code (the + scripted two-header path) returns ``None`` and the caller falls through to the existing + behavior.""" + if not is_bridge_authorization_code(code): + return None + decrypted = decrypt_value_helper( + code[len(_BRIDGE_AUTH_CODE_PREFIX) :], "bridge_authorization_code", return_original_value=False + ) + if not isinstance(decrypted, str): + return None + try: + return _BridgeAuthorizationCode.model_validate_json(decrypted) + except ValidationError: + return None + + +def _redirect_to_litellm_login(request: Request) -> RedirectResponse: + """Send an unauthenticated browser through litellm login before the interactive bridge authorize + can capture its identity. The bridge oauth_delegate flow seals the SSO user into the gateway code, + so a session is required; without one there is nothing to bind. After login the user re-initiates + the connection, which then finds the session cookie (the seamless return-to round-trip, which is + origin-validated against the control-plane URL, is a follow-up).""" + base_url = get_request_base_url(request) + return RedirectResponse(f"{base_url}/sso/key/generate") + + # LIT-4197: some upstream authorization servers reject an over-long ``state`` # (the encrypted OAuth session blob routinely exceeds their limit). The upstream # only needs an opaque value it echoes back on ``/callback``, so we forward a @@ -697,12 +769,31 @@ async def authorize_with_server( parsed = urlparse(redirect_uri) base_url = urlunparse(parsed._replace(query="")) request_base_url = get_request_base_url(request) + + # Interactive dcr_bridge oauth_delegate sign-in: this arm runs the gateway /callback and /token in + # the loop, so the gateway can capture the litellm user here (from the browser's UI session) and + # carry it to the back-channel token mint. Seal the SSO user and the target server into the state; + # the callback reads them back to mint the gateway authorization code. A DCR client cannot present a + # litellm key, so the browser session is the only identity source; without one there is nothing to + # bind, so send the user through login first. Every other oauth2 server keeps the identity-less state. + litellm_user_id: str | None = None + if mcp_server.is_dcr_bridge and mcp_server.is_oauth_delegate: + from litellm.proxy._experimental.mcp_server.byok_oauth_endpoints import ( # noqa: PLC0415 # inline import avoids a module-load circular import + _user_id_from_session_cookie, + ) + + litellm_user_id = _user_id_from_session_cookie(request) + if litellm_user_id is None: + return _redirect_to_litellm_login(request) + encoded_state = encode_state_with_base_url( base_url=base_url, original_state=state, code_challenge=code_challenge, code_challenge_method=code_challenge_method, client_redirect_uri=redirect_uri, + litellm_user_id=litellm_user_id, + mcp_server_id=mcp_server.server_id if litellm_user_id else None, ) relay_state = secrets.token_urlsafe(_OAUTH_STATE_HANDLE_BYTES) @@ -824,11 +915,14 @@ _BridgeMintError = Literal[ @dataclass(frozen=True, slots=True) class _BridgeMintReady: - """Everything the seal needs, resolved once before the exchange: the authorizing key hash and the - master-key-derived envelope keys. Passing this forward means identity resolution and key derivation - happen exactly once, and ``_finish_bridge_mint`` has no preconditions left that could fail.""" + """Everything the seal needs, resolved once before the exchange: the identity to bind the envelope + to and the master-key-derived envelope keys. The identity is a key_hash subject for the scripted + two-header client (resolved from the litellm key it presents) or a user_id subject for the + interactive SSO client (the user recovered from the gateway authorization code), so one phase-3 seal + serves both. Resolving identity here means ``_finish_bridge_mint`` has no preconditions left to + fail.""" - key_hash: str + identity: "EnvelopeIdentity" keys: "EnvelopeKeys" @@ -844,8 +938,8 @@ def _bridge_mint_error_response(error: _BridgeMintError) -> JSONResponse: status, code, desc = ( 400, "invalid_request", - "this server issues a gateway-bound credential; send a litellm credential " - "(x-litellm-api-key or Authorization) on the token request", + "this server issues a gateway-bound credential; complete the interactive sign-in, or " + "send a litellm credential (x-litellm-api-key or Authorization) on the token request", ) case "unsupported_grant": status, code, desc = ( @@ -923,18 +1017,30 @@ def _upstream_rejection_to_mint_error(rejection: _UpstreamGrantRejection) -> _Br assert_never(rejection) -async def _prepare_bridge_mint(request: Request, grant_type: str) -> "_BridgeMintReady | _BridgeMintError": +async def _prepare_bridge_mint( + request: Request, + grant_type: str, + mcp_server: MCPServer, + bridge_identity: _BridgeAuthorizationCode | None = None, +) -> "_BridgeMintReady | _BridgeMintError": """Phase 1, BEFORE the upstream exchange: reject a grant this mint does not support, confirm the gateway can mint (master_key set), resolve the litellm identity, and derive the envelope keys. Returns a ready context or a precise failure value. Running before the exchange is what makes every - failure here fail closed without consuming the single-use code or rotating a refresh token. A bridge - server issues only envelopes and seals no upstream refresh_token, so the client holds none to - present: the refresh_token grant is rejected up front rather than exchanged (which could rotate the - upstream credential) and its result then discarded. Identity-resolution failures keep their origin - so the mapper statuses each truthfully.""" + failure here fail closed without consuming the single-use code. + + Two identity sources, one envelope. The interactive DCR client authenticates via SSO at the bridged + authorize, so its identity arrives as ``bridge_identity`` (the user recovered from the gateway + authorization code) and mints a user subject. The scripted two-header client presents a litellm key + on the token request instead, so its identity is the active key's hash and mints a key_hash subject. + A missing or invalid presented key keeps its resolution origin so the mapper statuses it truthfully; + neither source present is ``no_identity``.""" from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( # noqa: PLC0415 # inline import avoids a module-load circular import envelope_keys_from_master_key, ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import + key_hash_identity, + user_identity, + ) from litellm.proxy.proxy_server import ( # noqa: PLC0415 # inline import avoids a module-load circular import master_key, ) @@ -943,16 +1049,21 @@ async def _prepare_bridge_mint(request: Request, grant_type: str) -> "_BridgeMin return "unsupported_grant" if not master_key: return "not_configured" + keys = envelope_keys_from_master_key(master_key) + if bridge_identity is not None: + identity = user_identity(server_id=mcp_server.server_id, user_id=bridge_identity.litellm_user_id) + return _BridgeMintReady(identity=identity, keys=keys) resolved = await _resolve_active_litellm_key(request) if not isinstance(resolved, _ResolvedKey): return _key_resolution_failure_to_mint_error(resolved) - return _BridgeMintReady(key_hash=resolved.key_hash, keys=envelope_keys_from_master_key(master_key)) + identity = key_hash_identity(server_id=mcp_server.server_id, key_hash=resolved.key_hash) + return _BridgeMintReady(identity=identity, keys=keys) def _finish_bridge_mint( ready: "_BridgeMintReady", mcp_server: MCPServer, token_response: object, now: datetime ) -> "JSONResponse | _BridgeMintError": - """Phase 3, AFTER the upstream exchange: seal the upstream grant into the client-held envelope using + """Phase 3, AFTER the upstream exchange: seal the upstream grant into the client-held envelope under the pre-resolved identity and keys, so the client holds one bearer that admits it and forwards the upstream token with nothing stored server-side. The only failures here are properties of the upstream response (no usable token, an already-expired lifetime, or a token too large to seal), @@ -963,14 +1074,12 @@ def _finish_bridge_mint( from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( # noqa: PLC0415 # inline import avoids a module-load circular import SealedEnvelope, UpstreamTokenGrant, - key_hash_identity, ) grant = _bridge_grant_from_token_response(token_response) if not isinstance(grant, UpstreamTokenGrant): return _upstream_rejection_to_mint_error(grant) - identity = key_hash_identity(server_id=mcp_server.server_id, key_hash=ready.key_hash) - sealed = build_bridge_token_response(identity, grant, ready.keys, now) + sealed = build_bridge_token_response(ready.identity, grant, ready.keys, now) if not isinstance(sealed, SealedEnvelope): return "too_large" # Report expires_in from the JWT's own second-truncated exp, rounding the elapsed portion up, so the @@ -1014,6 +1123,7 @@ async def exchange_token_with_server( except TokenEndpointAuthConfigError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc + bridge_identity: _BridgeAuthorizationCode | None = None if grant_type == "refresh_token": if not refresh_token: raise HTTPException( @@ -1033,6 +1143,19 @@ async def exchange_token_with_server( status_code=400, detail="code is required for authorization_code grant", ) + # Interactive dcr_bridge oauth_delegate: the client presents the gateway authorization code the + # callback sealed. Recover the SSO user and the real upstream code from it; the upstream exchange + # below uses the upstream code, and the mint binds the envelope to the recovered user. Bind the + # sealed server to this request so a code minted for one bridge server cannot be spent at another. + # A raw upstream code (scripted path) opens to None and the code is used as-is. + bridge_identity = open_bridge_authorization_code(code) + if bridge_identity is not None: + if bridge_identity.mcp_server_id != mcp_server.server_id: + raise HTTPException( + status_code=400, + detail="Authorization code was issued for a different MCP server", + ) + code = bridge_identity.upstream_code bridge_token_relay = _dcr_bridge_relays_client_registration(mcp_server) if bridge_token_relay and not redirect_uri: raise HTTPException( @@ -1058,7 +1181,7 @@ async def exchange_token_with_server( # phase 3. A failure here returns without ever touching the upstream credential. bridge_mint_ready: _BridgeMintReady | None = None if mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge: - prepared = await _prepare_bridge_mint(request, grant_type) + prepared = await _prepare_bridge_mint(request, grant_type, mcp_server, bridge_identity) if not isinstance(prepared, _BridgeMintReady): return _bridge_mint_error_response(prepared) bridge_mint_ready = prepared @@ -1706,7 +1829,20 @@ async def callback( # states while permitting same-origin / allowlisted clients. redirect_uri = _get_validated_client_redirect_uri(request, state_data) - params = {"code": code, "state": original_state} + # Interactive dcr_bridge oauth_delegate: the state carries the litellm user the authorize step + # captured. Instead of forwarding the raw upstream code (which the client would present at the + # token endpoint with no way to prove who signed in), seal the user and the upstream code into a + # gateway authorization code and forward THAT. The token endpoint decrypts it to bind the + # envelope to this user. Every other flow forwards the raw code unchanged. + litellm_user_id = state_data.get("litellm_user_id") + mcp_server_id = state_data.get("mcp_server_id") + forwarded_code = code + if isinstance(litellm_user_id, str) and litellm_user_id and isinstance(mcp_server_id, str) and mcp_server_id: + forwarded_code = seal_bridge_authorization_code( + upstream_code=code, litellm_user_id=litellm_user_id, mcp_server_id=mcp_server_id + ) + + params = {"code": forwarded_code, "state": original_state} complete_returned_url = _append_query_params(redirect_uri, params) response = RedirectResponse(url=complete_returned_url, status_code=302) _clear_oauth_state_cookie(response, request, state) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index e43312e400e..966619ee6b8 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -4365,7 +4365,7 @@ async def test_register_bridge_relay_never_persists(): _BRIDGE_MASTER_KEY = "sk-bridge-producer-master-key-0123456789abcdef" -async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_client_out=None): +async def _exchange_for_bridge_server(server, upstream_body, key_hash, code="auth-code", fake_client_out=None): from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( _ResolvedKey, exchange_token_with_server, @@ -4398,13 +4398,17 @@ async def _exchange_for_bridge_server(server, upstream_body, key_hash, fake_clie request=_bridge_mock_request(), mcp_server=server, grant_type="authorization_code", - code="auth-code", + code=code, redirect_uri="https://claude.ai/api/mcp/auth_callback", client_id="dcr-client-123", client_secret=None, code_verifier="verifier", ) - if server.is_oauth_delegate and server.is_dcr_bridge: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import is_bridge_authorization_code + + # The key_hash path resolves the presented litellm key; the interactive SSO path recovers identity + # from the gateway authorization code instead, so it never awaits the resolver. + if server.is_oauth_delegate and server.is_dcr_bridge and not is_bridge_authorization_code(code): key_resolver.assert_awaited_once() else: key_resolver.assert_not_awaited() @@ -4446,6 +4450,193 @@ async def test_oauth_delegate_bridge_token_exchange_mints_envelope_not_raw_token assert opened.upstream_authorization.get_secret_value() == "Bearer UPSTREAM-SECRET-TOKEN" +def test_bridge_authorization_code_round_trips_and_rejects_hostile_input(): + """The gateway authorization code seals and recovers the upstream code and the SSO user, and is + total over hostile input: a raw upstream code (scripted path) opens to None, and a tampered or + non-gateway value opens to None rather than raising.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + is_bridge_authorization_code, + open_bridge_authorization_code, + seal_bridge_authorization_code, + ) + + with patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY): + sealed = seal_bridge_authorization_code( + upstream_code="up-code", litellm_user_id="sso-user-9", mcp_server_id="srv-1" + ) + assert is_bridge_authorization_code(sealed) + opened = open_bridge_authorization_code(sealed) + assert opened is not None + assert opened.upstream_code == "up-code" + assert opened.litellm_user_id == "sso-user-9" + assert opened.mcp_server_id == "srv-1" + assert open_bridge_authorization_code("raw-upstream-code") is None + assert open_bridge_authorization_code(sealed[:-4] + "aaaa") is None + + +@pytest.mark.asyncio +async def test_interactive_bridge_token_exchange_mints_user_subject_envelope(): + """An interactive dcr_bridge oauth_delegate exchange (the client presents the gateway code the + callback sealed, and NO litellm key) mints an envelope bound to the SSO-captured user: it opens + to a user_id subject, and the upstream exchange used the real upstream code recovered from the + gateway code, not the sealed wrapper.""" + from datetime import datetime, timezone + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + seal_bridge_authorization_code, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + BridgeEnvelopeAdmitted, + envelope_keys_from_master_key, + resolve_bridge_envelope, + ) + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + with patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY): + gateway_code = seal_bridge_authorization_code( + upstream_code="REAL-UPSTREAM-CODE", litellm_user_id="sso-user-42", mcp_server_id=server.server_id + ) + upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + captured: dict = {} + response = await _exchange_for_bridge_server( + server, upstream, key_hash=None, code=gateway_code, fake_client_out=captured + ) + + token = json.loads(response.body)["access_token"] + keys = envelope_keys_from_master_key(_BRIDGE_MASTER_KEY) + opened = resolve_bridge_envelope(token, keys, datetime.now(timezone.utc), server.server_id) + assert isinstance(opened, BridgeEnvelopeAdmitted) + assert opened.identity.subject_type == "user_id" + assert opened.identity.subject == "sso-user-42" + assert opened.upstream_authorization.get_secret_value() == "Bearer UPSTREAM-SECRET-TOKEN" + assert captured["client"].post.call_args.kwargs["data"]["code"] == "REAL-UPSTREAM-CODE" + + +@pytest.mark.asyncio +async def test_interactive_bridge_gateway_code_for_another_server_is_rejected_400(): + """A gateway authorization code is bound to the server it was minted for: presenting it at another + server's token endpoint is a 400, so a code cannot be replayed across a server boundary.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + seal_bridge_authorization_code, + ) + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate) + with patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY): + gateway_code = seal_bridge_authorization_code( + upstream_code="up-code", litellm_user_id="sso-user-42", mcp_server_id="a-different-server-id" + ) + upstream = {"access_token": "UPSTREAM-SECRET-TOKEN", "token_type": "Bearer", "expires_in": 3600} + with pytest.raises(HTTPException) as exc: + await _exchange_for_bridge_server(server, upstream, key_hash=None, code=gateway_code) + assert exc.value.status_code == 400 + + +@pytest.mark.asyncio +async def test_interactive_bridge_authorize_seals_sso_user_into_state(): + """On the short-circuit bridge oauth_delegate arm, authorize captures the SSO user from the UI + session cookie and seals it (and the target server) into the encrypted OAuth state, so the + callback can later mint a user-bound gateway code; it still proceeds to the upstream redirect.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import authorize_with_server + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate, client_id="admin-client", registration_url=None) + captured: dict = {} + + def _capture(**kwargs): + captured.update(kwargs) + return "mocked_encrypted_state" + + with ( + patch( + "litellm.proxy._experimental.mcp_server.byok_oauth_endpoints._user_id_from_session_cookie", + return_value="sso-user-42", + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encode_state_with_base_url", + side_effect=_capture, + ), + ): + response = await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=server, + client_id="ignored", + redirect_uri="http://127.0.0.1:60108/callback", + state="s", + code_challenge="chal", + code_challenge_method="S256", + ) + + assert captured["litellm_user_id"] == "sso-user-42" + assert captured["mcp_server_id"] == server.server_id + assert "/sso/key/generate" not in response.headers["location"] + + +@pytest.mark.asyncio +async def test_interactive_bridge_authorize_without_session_redirects_to_login(): + """Without a UI session there is no identity to bind, so the short-circuit bridge oauth_delegate + authorize sends the browser through litellm login instead of proceeding to the upstream.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import authorize_with_server + from litellm.types.mcp import MCPAuth + + server = _bridge_server(auth_type=MCPAuth.oauth_delegate, client_id="admin-client", registration_url=None) + with patch( + "litellm.proxy._experimental.mcp_server.byok_oauth_endpoints._user_id_from_session_cookie", + return_value=None, + ): + response = await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=server, + client_id="ignored", + redirect_uri="http://127.0.0.1:60108/callback", + state="s", + code_challenge="chal", + code_challenge_method="S256", + ) + assert "/sso/key/generate" in response.headers["location"] + + +@pytest.mark.asyncio +async def test_interactive_bridge_callback_seals_user_into_gateway_code(): + """When the OAuth state carries the captured SSO user, the callback forwards a gateway + authorization code (sealing the user and upstream code) to the client instead of the raw upstream + code, so the client's later token call can prove who signed in.""" + from urllib.parse import parse_qs, urlparse + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + callback, + is_bridge_authorization_code, + ) + + state_data = { + "original_state": "client-state", + "client_redirect_uri": "http://127.0.0.1:60108/cb", + "base_url": "http://127.0.0.1:60108/cb", + "litellm_user_id": "sso-user-42", + "mcp_server_id": "bridge_srv", + } + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._resolve_encoded_oauth_state", + return_value="enc", + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash", + return_value=state_data, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._get_validated_client_redirect_uri", + return_value="http://127.0.0.1:60108/cb", + ), + patch("litellm.proxy.proxy_server.master_key", _BRIDGE_MASTER_KEY), + ): + response = await callback(request=_bridge_mock_request(), code="REAL-UPSTREAM-CODE", state="relay") + + forwarded_code = parse_qs(urlparse(response.headers["location"]).query)["code"][0] + assert is_bridge_authorization_code(forwarded_code) + + @pytest.mark.asyncio async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm_identity(): """Without a resolvable litellm identity on the token request, the exchange must not mint an @@ -4727,10 +4918,11 @@ def test_bridge_reported_expires_in_can_be_zero_at_jwt_exp_boundary(): from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( envelope_keys_from_master_key, ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import key_hash_identity from litellm.types.mcp import MCPAuth ready = _BridgeMintReady( - key_hash="hashed-litellm-key-77", + identity=key_hash_identity(server_id="bridge_srv", key_hash="hashed-litellm-key-77"), keys=envelope_keys_from_master_key(_BRIDGE_MASTER_KEY), ) response = _finish_bridge_mint( From f96899ae2b793f049a01a91db648980cd85021d2 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Mon, 13 Jul 2026 11:08:08 -0700 Subject: [PATCH 323/399] fix(mcp): classify the user-subject reload's errors like the key path (503 outage, 401 missing) _reload_admitted_user mirrored only part of _reload_admitted_key's error contract: it caught ProxyException and HTTPException but had no arm for anything else, so a transient DB outage surfaced as an opaque 500 instead of the retryable 503 the key path guarantees, and a missing user surfaced as a 500 too. The missing-user case is the subtle one: get_user_object raises a bare Exception for a deleted user (not a ProxyException like get_key_object does for a missing key), so the ProxyException/HTTPException clause never caught it and the user_object-is-None branch it was supposed to hit is unreachable on the production path. Add the same except-Exception arm the key path uses, with the one deliberate difference the differing get_user_object contract requires: a database-service-unavailable error still raises the retryable 503, while a missing user or any other non-outage resolution failure fails closed as a 401 rather than propagating as a 500. The regression tests now drive the real behavior (get_user_object raising) rather than a None return that never happens in production, and cover both the 503 outage and the 401 missing-user paths. --- .../mcp_server/auth/user_api_key_auth_mcp.py | 13 +++++-- .../auth/test_user_api_key_auth_mcp.py | 34 +++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index faec35db41a..01861abbbc0 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -605,8 +605,14 @@ class MCPRequestHandler: caller's centralized policy gate then enforces the user's live budget and org state, and a SCIM-deactivated owner fails closed here exactly as the key path enforces it. No team is bound; a user may belong to many teams or none, so the envelope grants the - user's own access rather than silently selecting one team's scope. A missing user - fails closed with a 401 rather than admitting an unresolved identity.""" + user's own access rather than silently selecting one team's scope. + + Error handling mirrors the key path's retryable-503 contract, with one deliberate + difference: ``get_key_object`` raises a ``ProxyException`` for a missing key, but + ``get_user_object`` raises a bare ``Exception`` for a missing user (it does not surface as a + ``ProxyException``/``HTTPException``). So a transient DB outage still surfaces as a retryable + 503 via ``_raise_503_if_db_unavailable``, while a missing user, or any other non-outage + resolution failure, fails closed as a 401 rather than propagating as an opaque 500.""" from litellm.proxy.auth.auth_checks import get_user_object from litellm.proxy.proxy_server import prisma_client, user_api_key_cache @@ -621,6 +627,9 @@ class MCPRequestHandler: ) except (ProxyException, HTTPException): raise HTTPException(status_code=401, detail="Invalid or expired credential") from None + except Exception as e: # noqa: BLE001 # DB outage -> retryable 503; a missing user (bare Exception) or any other resolution failure -> fail closed 401, never an opaque 500 + MCPRequestHandler._raise_503_if_db_unavailable(e) + raise HTTPException(status_code=401, detail="Invalid or expired credential") from None if user_object is None: raise HTTPException(status_code=401, detail="Invalid or expired credential") if isinstance(user_object.metadata, dict) and user_object.metadata.get("scim_active") is False: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index a6affe5496c..a441e6a154b 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -5117,8 +5117,10 @@ class TestMCPDcrBridgeDelegateAdmission: } async def test_user_subject_envelope_missing_user_fails_closed_401(self): - """A user_id envelope whose user has since been deleted must fail closed: get_user_object - resolves None, so admission 401s instead of admitting an unresolved identity.""" + """A user_id envelope whose user has since been deleted must fail closed with a 401, not a 500. + get_user_object raises a bare Exception for a missing user (it does not return None on the + production path), so the reload must catch it and fail closed rather than let it propagate as an + opaque 500. Regression for the missing-user path surfacing as a 500.""" envelope = self._mint_bridge_envelope(user_id="ghost-user") scope = { "type": "http", @@ -5129,7 +5131,7 @@ class TestMCPDcrBridgeDelegateAdmission: with ( patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), - self._patch_user_reload(return_value=None), + self._patch_user_reload(side_effect=Exception("user not found")), ): mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() with pytest.raises(HTTPException) as exc_info: @@ -5137,6 +5139,28 @@ class TestMCPDcrBridgeDelegateAdmission: assert exc_info.value.status_code == 401 + async def test_user_subject_envelope_db_outage_is_retryable_503(self): + """A transient database outage while reloading the envelope's user is a retryable 503, not an + opaque 500, matching the key path's contract so an interactive DCR client retries instead of + treating a live identity as invalid. Regression for the user reload dropping the 503 arm.""" + envelope = self._mint_bridge_envelope(user_id="sso-user-7") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_user_reload(side_effect=ConnectionError("auth database unreachable")), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 503 + async def test_user_subject_envelope_scim_deactivated_user_fails_closed_401(self): """SCIM-deactivating the envelope's user revokes it immediately: the reloaded user carries scim_active False, so admission 401s rather than letting an offboarded user keep tool access @@ -5151,9 +5175,7 @@ class TestMCPDcrBridgeDelegateAdmission: with ( patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), - self._patch_user_reload( - return_value=MagicMock(user_id="offboarded-user", metadata={"scim_active": False}) - ), + self._patch_user_reload(return_value=MagicMock(user_id="offboarded-user", metadata={"scim_active": False})), ): mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() with pytest.raises(HTTPException) as exc_info: From c46863b0e64a4962b84ddf41dc1a9faf7faac3dd Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Mon, 13 Jul 2026 11:18:53 -0700 Subject: [PATCH 324/399] fix(mcp): admit a user-subject envelope with the user's own MCP object permission _reload_admitted_user returned a bare UserAPIKeyAuth(user_id=...), so the shared get_allowed_mcp_servers found no key/team/object-permission grants and an interactive SSO client could admit successfully yet see zero tools on a normal (allow_all_keys=False) server. The key path returns the full key record whose object permission drives that computation; the user path dropped it. Resolve the user's own MCP object permission and put it on the returned auth, so the same get_allowed_mcp_servers the key path uses grants the user their litellm-granted servers and access groups. This reuses get_object_permission (the id-to-grants resolver keys and teams already use) and does not duplicate any permission logic; get_user_object does not load object_permission, so it is resolved from the user's object_permission_id the same way the key and team paths do. Only the user's own object permission is bound. A UserAPIKeyAuth carries a single team_id while a user may belong to many teams, so team-inherited MCP grants for a user are a follow-up: they need a many-teams union get_allowed_mcp_servers does not do off one auth object, and faking one here would be the kind of half-measure that spawns more bugs. Tests cover the user's object permission riding onto the admitted auth, and the existing admit/SCIM/missing-user/503 cases still hold. --- .../mcp_server/auth/user_api_key_auth_mcp.py | 32 ++++++++++--- .../auth/test_user_api_key_auth_mcp.py | 46 ++++++++++++++++++- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index 01861abbbc0..ac3e4439fa1 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -601,11 +601,14 @@ class MCPRequestHandler: The DCR client authenticates via SSO at the bridged authorize, which yields a user subject rather than a virtual key, so the envelope admits under the user's own - identity: the reloaded ``user_id`` rides on the returned ``UserAPIKeyAuth`` and the - caller's centralized policy gate then enforces the user's live budget and org state, - and a SCIM-deactivated owner fails closed here exactly as the key path enforces it. No - team is bound; a user may belong to many teams or none, so the envelope grants the - user's own access rather than silently selecting one team's scope. + identity: the reloaded ``user_id`` and the user's own MCP object permission ride on the + returned ``UserAPIKeyAuth``, and the SAME ``get_allowed_mcp_servers`` the key path uses then + computes which servers the user may reach, so the user's litellm MCP grants and access groups + gate the request exactly as a key's do. Only the user's OWN object permission is bound: a + ``UserAPIKeyAuth`` carries a single ``team_id`` while a user may belong to many teams, so + team-inherited MCP grants for a user are a follow-up (they need a many-teams union + ``get_allowed_mcp_servers`` does not do off one auth object). The caller's centralized policy + gate enforces the user's live budget and org state, and a SCIM-deactivated owner fails closed. Error handling mirrors the key path's retryable-503 contract, with one deliberate difference: ``get_key_object`` raises a ``ProxyException`` for a missing key, but @@ -613,7 +616,7 @@ class MCPRequestHandler: ``ProxyException``/``HTTPException``). So a transient DB outage still surfaces as a retryable 503 via ``_raise_503_if_db_unavailable``, while a missing user, or any other non-outage resolution failure, fails closed as a 401 rather than propagating as an opaque 500.""" - from litellm.proxy.auth.auth_checks import get_user_object + from litellm.proxy.auth.auth_checks import get_object_permission, get_user_object from litellm.proxy.proxy_server import prisma_client, user_api_key_cache if prisma_client is None: @@ -634,7 +637,22 @@ class MCPRequestHandler: raise HTTPException(status_code=401, detail="Invalid or expired credential") if isinstance(user_object.metadata, dict) and user_object.metadata.get("scim_active") is False: raise HTTPException(status_code=401, detail="Invalid or expired credential") - return UserAPIKeyAuth(user_id=user_object.user_id) + # Resolve the user's own MCP object permission (get_user_object does not load it) so the shared + # get_allowed_mcp_servers can grant the user their litellm-granted servers. Reuses the same + # get_object_permission resolver the key and team paths use; no permission logic is duplicated. + object_permission = user_object.object_permission + if user_object.object_permission_id and object_permission is None: + object_permission = await get_object_permission( + object_permission_id=user_object.object_permission_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + ) + return UserAPIKeyAuth( + user_id=user_object.user_id, + user_role=user_object.user_role, + object_permission=object_permission, + object_permission_id=user_object.object_permission_id, + ) @staticmethod async def _reload_admitted_key(key_hash: str) -> UserAPIKeyAuth: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index a441e6a154b..0b1905689ac 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -5103,7 +5103,13 @@ class TestMCPDcrBridgeDelegateAdmission: patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), self._patch_user_reload( - return_value=MagicMock(user_id="sso-user-7", metadata={"scim_active": True}) + return_value=MagicMock( + user_id="sso-user-7", + metadata={"scim_active": True}, + user_role=None, + object_permission=None, + object_permission_id=None, + ) ) as get_user_object, ): mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() @@ -5116,6 +5122,44 @@ class TestMCPDcrBridgeDelegateAdmission: "bridge_delegate_server": {"Authorization": "Bearer inner-upstream-access-token"} } + async def test_user_subject_envelope_carries_the_users_mcp_object_permission(self): + """The admitted user's own MCP object permission rides on the returned auth so the shared + get_allowed_mcp_servers grants the user their litellm-granted servers, rather than admitting a + bare user with no MCP access. Regression for the signed-in SSO client getting zero tools because + the reload dropped the user's object permission.""" + object_permission = LiteLLM_ObjectPermissionTable( + object_permission_id="op-user-7", mcp_servers=["bridge_delegate_server"] + ) + envelope = self._mint_bridge_envelope(user_id="sso-user-7") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ), + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_user_reload( + return_value=MagicMock( + user_id="sso-user-7", + metadata={"scim_active": True}, + user_role=None, + object_permission=object_permission, + object_permission_id="op-user-7", + ) + ), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + (auth_result, _h, _s, _headers, _o, _r) = await MCPRequestHandler.process_mcp_request(scope) + + assert auth_result.object_permission is not None + assert auth_result.object_permission.mcp_servers == ["bridge_delegate_server"] + async def test_user_subject_envelope_missing_user_fails_closed_401(self): """A user_id envelope whose user has since been deleted must fail closed with a 401, not a 500. get_user_object raises a bare Exception for a missing user (it does not return None on the From 7fce761cdeca0ec8431e1b8feb1b8192df92521a Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 13 Jul 2026 12:19:38 -0700 Subject: [PATCH 325/399] fix(ui): respect litellm_key_header_name in BYOK credential save and workflow runs fetches (#33103) --- ui/litellm-dashboard/eslint-suppressions.json | 299 +++++++++--------- .../mcp-servers/_components/mcp_servers.tsx | 1 - .../playground/components/chat_ui/ChatUI.tsx | 1 - .../workflows/WorkflowRuns.test.tsx | 19 +- .../(dashboard)/workflows/WorkflowRuns.tsx | 8 +- .../mcp_tools/ByokCredentialModal.test.tsx | 72 +++++ .../mcp_tools/ByokCredentialModal.tsx | 37 +-- .../src/components/networking.test.ts | 64 ++++ .../src/components/networking.tsx | 3 + 9 files changed, 324 insertions(+), 180 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.test.tsx diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index ba2e2a375ca..953de4fe480 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -515,6 +515,152 @@ "count": 2 } }, + "src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx": { + "unused-imports/no-unused-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/MCPNetworkSettings.tsx": { + "react-hooks/immutability": { + "count": 2 + } + }, + "src/app/(dashboard)/mcp-servers/_components/MCPSubmissionsTab.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + }, + "unused-imports/no-unused-imports": { + "count": 2 + } + }, + "src/app/(dashboard)/mcp-servers/_components/OAuthFormFields.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/OpenAPIQuickPicker.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/ToolTestPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, + "src/app/(dashboard)/mcp-servers/_components/create_mcp_server.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 4 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/static-components": { + "count": 4 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx": { + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 5 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, "src/app/(dashboard)/memory/_components/MemoryView.tsx": { "react-hooks/set-state-in-effect": { "count": 1 @@ -1860,45 +2006,11 @@ "count": 1 } }, - "src/components/mcp_tools/ByokCredentialModal.tsx": { - "no-restricted-syntax": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx": { - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/MCPNetworkSettings.tsx": { - "react-hooks/immutability": { - "count": 2 - } - }, - "src/app/(dashboard)/mcp-servers/_components/MCPSubmissionsTab.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/components/mcp_tools/MCPToolArgumentsForm.tsx": { "no-nested-ternary": { "count": 5 } }, - "src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx": { - "no-nested-ternary": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - }, - "unused-imports/no-unused-imports": { - "count": 2 - } - }, "src/components/mcp_tools/McpCrudPermissionPanel.tsx": { "no-nested-ternary": { "count": 3 @@ -1907,123 +2019,6 @@ "count": 1 } }, - "src/app/(dashboard)/mcp-servers/_components/OAuthFormFields.tsx": { - "no-nested-ternary": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/OpenAPIQuickPicker.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/ToolTestPanel.tsx": { - "no-nested-ternary": { - "count": 3 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx": { - "no-nested-ternary": { - "count": 2 - } - }, - "src/app/(dashboard)/mcp-servers/_components/create_mcp_server.tsx": { - "no-nested-ternary": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 4 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/static-components": { - "count": 4 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.tsx": { - "no-nested-ternary": { - "count": 3 - }, - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx": { - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx": { - "no-nested-ternary": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 5 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx": { - "no-nested-ternary": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx": { - "no-nested-ternary": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, "src/components/model_add/AddCredentialModal.tsx": { "no-restricted-imports": { "count": 1 @@ -2545,4 +2540,4 @@ "count": 1 } } -} +} \ No newline at end of file diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx index 0afb4bd9314..f186fef22da 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx @@ -684,7 +684,6 @@ const MCPServers: React.FC = ({ accessToken, userRole, userID }) refetch(); setByokModalServer(null); }} - accessToken={accessToken || ""} /> )} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx index 689abf66b41..5133fafb4a7 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx @@ -2186,7 +2186,6 @@ const ChatUI: React.FC = ({ loadMCPServers(); setByokModalServer(null); }} - accessToken={accessToken || ""} /> )} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx index e73abfe7cd6..1aee3fcc8ab 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.test.tsx @@ -4,7 +4,10 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import WorkflowRuns from "./WorkflowRuns"; -vi.mock("@/components/networking", () => ({ proxyBaseUrl: "" })); +vi.mock("@/components/networking", () => ({ + proxyBaseUrl: "", + getGlobalLitellmHeaderName: () => "x-litellm-api-key", +})); interface FakeRun { run_id: string; @@ -78,4 +81,18 @@ describe("WorkflowRuns (migrated onto shared DataTable)", () => { expect(await screen.findByText("No workflow runs yet")).toBeInTheDocument(); }); + + it("sends the configured litellm key header on every fetch instead of hardcoding Authorization", async () => { + const user = userEvent.setup(); + const fetchSpy = mockFetch(RUNS); + vi.stubGlobal("fetch", fetchSpy); + render(); + + await user.click(await screen.findByText("First run")); + + await waitFor(() => expect(fetchSpy).toHaveBeenCalledTimes(3)); + for (const [url, init] of fetchSpy.mock.calls as [string, RequestInit][]) { + expect(init.headers, url).toEqual({ "x-litellm-api-key": "Bearer tok" }); + } + }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx index 9afa07251c2..7354b7479f4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/workflows/WorkflowRuns.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback, useMemo } from "react"; import { Button, Collapse, Drawer, Empty, Spin, Tooltip, Typography } from "antd"; import { ReloadOutlined } from "@ant-design/icons"; import type { ColumnDef, ColumnFiltersState } from "@tanstack/react-table"; -import { proxyBaseUrl } from "@/components/networking"; +import { getGlobalLitellmHeaderName, proxyBaseUrl } from "@/components/networking"; import { DataTable, DataTableFilterDrawer, @@ -507,7 +507,7 @@ const WorkflowRuns: React.FC = ({ accessToken }) => { setLoadingRuns(true); try { const res = await fetch(`${proxyBaseUrl ?? ""}/v1/workflows/runs?limit=100`, { - headers: { Authorization: `Bearer ${accessToken}` }, + headers: { [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}` }, }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); @@ -531,10 +531,10 @@ const WorkflowRuns: React.FC = ({ accessToken }) => { const base = proxyBaseUrl ?? ""; const [evRes, msgRes] = await Promise.all([ fetch(`${base}/v1/workflows/runs/${run.run_id}/events`, { - headers: { Authorization: `Bearer ${accessToken}` }, + headers: { [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}` }, }), fetch(`${base}/v1/workflows/runs/${run.run_id}/messages`, { - headers: { Authorization: `Bearer ${accessToken}` }, + headers: { [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}` }, }), ]); const evData = evRes.ok ? await evRes.json() : { events: [] }; diff --git a/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.test.tsx new file mode 100644 index 00000000000..021aec5f85f --- /dev/null +++ b/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.test.tsx @@ -0,0 +1,72 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { registerAuthHeaderNameGetter, registerAuthTokenGetter, registerBaseUrlGetter } from "@/lib/http/runtime"; +import { ByokCredentialModal } from "./ByokCredentialModal"; +import type { MCPServer } from "./types"; + +const fetchSpy = vi.hoisted(() => { + const spy = vi.fn<(request: Request) => Promise>(); + vi.stubGlobal("fetch", spy); + return spy; +}); + +vi.mock("@/components/molecules/message_manager", () => ({ + default: { success: vi.fn(), error: vi.fn() }, +})); + +const SERVER = { server_id: "srv-1", alias: "Linear", server_name: "Linear" } as MCPServer; + +const jsonResponse = (body: unknown, status = 200) => + new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } }); + +async function fillAndSubmit(user: ReturnType) { + await user.click(screen.getByText("Continue to Authentication")); + await user.type(screen.getByPlaceholderText("Enter your API key"), "linear-key"); + await user.click(screen.getByRole("button", { name: /Connect & Authorize/ })); +} + +beforeEach(() => { + fetchSpy.mockReset(); + registerBaseUrlGetter(() => ""); + registerAuthTokenGetter(() => "sk-session"); +}); + +describe("ByokCredentialModal", () => { + it("saves the credential with the session's configured litellm key header, not a hardcoded Authorization", async () => { + registerAuthHeaderNameGetter(() => "x-litellm-api-key"); + fetchSpy.mockResolvedValue(jsonResponse({ server_id: "srv-1", has_credential: true })); + const onSuccess = vi.fn(); + const user = userEvent.setup(); + render( {}} onSuccess={onSuccess} />); + + await fillAndSubmit(user); + + await waitFor(() => expect(onSuccess).toHaveBeenCalledWith("srv-1")); + const request = fetchSpy.mock.calls[0][0]; + expect(request.method).toBe("POST"); + expect(new URL(request.url).pathname).toBe("/v1/mcp/server/srv-1/user-credential"); + expect(request.headers.get("x-litellm-api-key")).toBe("Bearer sk-session"); + expect(request.headers.get("Authorization")).toBeNull(); + expect(await request.json()).toEqual({ credential: "linear-key", save: true }); + }); + + it("surfaces the backend's detail.error message when the save fails", async () => { + registerAuthHeaderNameGetter(() => "Authorization"); + fetchSpy.mockResolvedValue( + jsonResponse({ detail: { error: "This MCP server does not support BYOK credentials" } }, 400), + ); + const MessageManager = (await import("@/components/molecules/message_manager")).default; + const onSuccess = vi.fn(); + const user = userEvent.setup(); + render( {}} onSuccess={onSuccess} />); + + await fillAndSubmit(user); + + await waitFor(() => + expect(MessageManager.error).toHaveBeenCalledWith("This MCP server does not support BYOK credentials"), + ); + expect(onSuccess).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx b/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx index cb07db871fd..f36de019aa5 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx @@ -3,6 +3,8 @@ import React, { useState } from "react"; import { Modal, Input, Switch } from "antd"; import MessageManager from "@/components/molecules/message_manager"; +import { fetchClient } from "@/lib/http/api"; +import { ApiError } from "@/lib/http/client"; import { KeyOutlined, LockOutlined, @@ -14,21 +16,22 @@ import { } from "@ant-design/icons"; import { MCPServer } from "./types"; +const byokSaveErrorMessage = (e: unknown): string => { + if (e instanceof ApiError) { + const detail = (e.body as { detail?: { error?: string } } | null)?.detail?.error; + if (detail) return detail; + } + return e instanceof Error && e.message ? e.message : "Failed to connect"; +}; + interface ByokCredentialModalProps { server: MCPServer; open: boolean; onClose: () => void; onSuccess: (serverId: string) => void; - accessToken: string; } -export const ByokCredentialModal: React.FC = ({ - server, - open, - onClose, - onSuccess, - accessToken, -}) => { +export const ByokCredentialModal: React.FC = ({ server, open, onClose, onSuccess }) => { const [step, setStep] = useState<1 | 2>(1); const [apiKey, setApiKey] = useState(""); const [saveKey, setSaveKey] = useState(true); @@ -52,23 +55,15 @@ export const ByokCredentialModal: React.FC = ({ } setLoading(true); try { - const response = await fetch(`/v1/mcp/server/${server.server_id}/user-credential`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, - }, - body: JSON.stringify({ credential: apiKey.trim(), save: saveKey }), + await fetchClient.POST("/v1/mcp/server/{server_id}/user-credential", { + params: { path: { server_id: server.server_id } }, + body: { credential: apiKey.trim(), save: saveKey }, }); - if (!response.ok) { - const err = await response.json(); - throw new Error(err?.detail?.error || "Failed to save credential"); - } MessageManager.success(`Connected to ${serverDisplayName}`); onSuccess(server.server_id); handleClose(); - } catch (e: any) { - MessageManager.error(e.message || "Failed to connect"); + } catch (e) { + MessageManager.error(byokSaveErrorMessage(e)); } finally { setLoading(false); } diff --git a/ui/litellm-dashboard/src/components/networking.test.ts b/ui/litellm-dashboard/src/components/networking.test.ts index b9d04a00f61..e6ee4de2735 100644 --- a/ui/litellm-dashboard/src/components/networking.test.ts +++ b/ui/litellm-dashboard/src/components/networking.test.ts @@ -530,3 +530,67 @@ describe("buildModelGroupTestRequest", () => { expect(body).toEqual({ model: "text-embedding-3-small", input: "test from litellm" }); }); }); + +describe("testMCPToolsListRequest auth headers", () => { + const originalFetch = global.fetch; + + const captureFetch = () => { + const mockFetch = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + headers: { get: () => "application/json" }, + json: vi.fn().mockResolvedValue({ tools: [] }), + } as any); + global.fetch = mockFetch as any; + return mockFetch; + }; + + const sentHeaders = (mockFetch: ReturnType): Record => + (mockFetch.mock.calls[0][1] as RequestInit).headers as Record; + + afterEach(() => { + Networking.setGlobalLitellmHeaderName("Authorization"); + global.fetch = originalFetch; + }); + + it("sends the litellm key under a custom litellm_key_header_name even when an upstream OAuth token uses Authorization", async () => { + Networking.setGlobalLitellmHeaderName("x-litellm-key"); + const mockFetch = captureFetch(); + + await Networking.testMCPToolsListRequest("sk-key", {}, "upstream-oauth-token"); + + const headers = sentHeaders(mockFetch); + expect(headers["x-litellm-key"]).toBe("Bearer sk-key"); + expect(headers["Authorization"]).toBe("Bearer upstream-oauth-token"); + }); + + it("Bearer-prefixes x-litellm-api-key when it is the configured key header (raw values fail _get_bearer_token)", async () => { + Networking.setGlobalLitellmHeaderName("x-litellm-api-key"); + const mockFetch = captureFetch(); + + await Networking.testMCPToolsListRequest("sk-key", {}, "upstream-oauth-token"); + + const headers = sentHeaders(mockFetch); + expect(headers["x-litellm-api-key"]).toBe("Bearer sk-key"); + expect(headers["Authorization"]).toBe("Bearer upstream-oauth-token"); + }); + + it("never clobbers the upstream OAuth token on default deployments", async () => { + const mockFetch = captureFetch(); + + await Networking.testMCPToolsListRequest("sk-key", {}, "upstream-oauth-token"); + + const headers = sentHeaders(mockFetch); + expect(headers["Authorization"]).toBe("Bearer upstream-oauth-token"); + expect(headers["x-litellm-api-key"]).toBe("sk-key"); + }); + + it("sends the litellm key as the bearer on default deployments without an OAuth token", async () => { + const mockFetch = captureFetch(); + + await Networking.testMCPToolsListRequest("sk-key", {}); + + const headers = sentHeaders(mockFetch); + expect(headers["Authorization"]).toBe("Bearer sk-key"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 10d7f12604d..875a345a4c5 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -6650,6 +6650,9 @@ export const testMCPToolsListRequest = async ( }; if (accessToken) { headers["x-litellm-api-key"] = accessToken; + if (globalLitellmHeaderName.toLowerCase() !== "authorization") { + headers[globalLitellmHeaderName] = `Bearer ${accessToken}`; + } } if (oauthAccessToken) { headers["Authorization"] = `Bearer ${oauthAccessToken}`; From aa9dcb43cfaee139ad515ea2a66eab0c31f04a6c Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 13 Jul 2026 12:19:57 -0700 Subject: [PATCH 326/399] refactor(ui): standardize debounce waits behind shared DEBOUNCE_WAIT_MS constant (#33040) --- .../usage/_components/components/UsagePageView.tsx | 3 ++- .../src/app/(dashboard)/users/_components/view_users.tsx | 3 ++- .../PaginatedKeyAliasSelect/PaginatedKeyAliasSelect.tsx | 4 ++-- .../ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx | 4 ++-- .../src/components/VirtualKeysPage/VirtualKeysTable.tsx | 3 ++- .../src/components/common_components/team_dropdown.tsx | 4 ++-- .../src/components/common_components/team_multi_select.tsx | 4 ++-- .../src/components/team/TeamVirtualKeysTable.tsx | 3 ++- ui/litellm-dashboard/src/utils/debounceConstants.ts | 1 + 9 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 ui/litellm-dashboard/src/utils/debounceConstants.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx index a95d48aa75b..d2f75609d18 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx @@ -8,6 +8,7 @@ import { DownOutlined, ExportOutlined, InfoCircleOutlined, LoadingOutlined, RightOutlined } from "@ant-design/icons"; import { useDebouncedState } from "@tanstack/react-pacer/debouncer"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { Card, Col, @@ -94,7 +95,7 @@ const UsagePage: React.FC = ({ teams, organizations }) => { // Debounced search for user selector const [userSearchInput, setUserSearchInput] = useState(""); const [debouncedUserSearch, setDebouncedUserSearch] = useDebouncedState("", { - wait: 300, + wait: DEBOUNCE_WAIT_MS, }); const { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.tsx index 18709e05df8..db3b17d6af3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.tsx @@ -16,6 +16,7 @@ import { import OnboardingModal, { InvitationLink } from "@/components/onboarding_link"; import { updateExistingKeys } from "@/utils/dataUtils"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { isAdminRole, isProxyAdminRole } from "@/utils/roles"; import { useDebouncedState } from "@tanstack/react-pacer/debouncer"; import { useQuery, useQueryClient } from "@tanstack/react-query"; @@ -86,7 +87,7 @@ const ViewUserDashboard: React.FC = ({ const [userToDelete, setUserToDelete] = useState(null); const [activeTab, setActiveTab] = useState("users"); const [filters, setFilters] = useState(initialFilters); - const [debouncedFilters, setDebouncedFilters, debouncer] = useDebouncedState(filters, { wait: 300 }); + const [debouncedFilters, setDebouncedFilters, debouncer] = useDebouncedState(filters, { wait: DEBOUNCE_WAIT_MS }); const [isInvitationLinkModalVisible, setIsInvitationLinkModalVisible] = useState(false); const [invitationLinkData, setInvitationLinkData] = useState(null); const [baseUrl, setBaseUrl] = useState(null); diff --git a/ui/litellm-dashboard/src/components/KeyAliasSelect/PaginatedKeyAliasSelect/PaginatedKeyAliasSelect.tsx b/ui/litellm-dashboard/src/components/KeyAliasSelect/PaginatedKeyAliasSelect/PaginatedKeyAliasSelect.tsx index d42d5ab324a..1d19ba3255d 100644 --- a/ui/litellm-dashboard/src/components/KeyAliasSelect/PaginatedKeyAliasSelect/PaginatedKeyAliasSelect.tsx +++ b/ui/litellm-dashboard/src/components/KeyAliasSelect/PaginatedKeyAliasSelect/PaginatedKeyAliasSelect.tsx @@ -1,4 +1,5 @@ import { useInfiniteKeyAliases } from "@/app/(dashboard)/hooks/keys/useKeyAliases"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { LoadingOutlined } from "@ant-design/icons"; import { useDebouncedState } from "@tanstack/react-pacer/debouncer"; import { Select } from "antd"; @@ -16,7 +17,6 @@ export interface PaginatedKeyAliasSelectProps { } const SCROLL_THRESHOLD = 0.8; -const DEBOUNCE_MS = 300; export const PaginatedKeyAliasSelect = ({ value, @@ -30,7 +30,7 @@ export const PaginatedKeyAliasSelect = ({ }: PaginatedKeyAliasSelectProps) => { const [searchInput, setSearchInput] = useState(""); const [debouncedSearch, setDebouncedSearch] = useDebouncedState("", { - wait: DEBOUNCE_MS, + wait: DEBOUNCE_WAIT_MS, }); const teamId = allFilters?.["Team ID"] || undefined; diff --git a/ui/litellm-dashboard/src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx b/ui/litellm-dashboard/src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx index da3ecf77ded..a77b2cf561e 100644 --- a/ui/litellm-dashboard/src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx +++ b/ui/litellm-dashboard/src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx @@ -1,4 +1,5 @@ import { useInfiniteModelInfo } from "@/app/(dashboard)/hooks/models/useModels"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { LoadingOutlined } from "@ant-design/icons"; import { useDebouncedState } from "@tanstack/react-pacer/debouncer"; import { Select, Space, Typography } from "antd"; @@ -17,7 +18,6 @@ export interface PaginatedModelSelectProps { } const SCROLL_THRESHOLD = 0.8; -const DEBOUNCE_MS = 300; export const PaginatedModelSelect = ({ value, @@ -30,7 +30,7 @@ export const PaginatedModelSelect = ({ }: PaginatedModelSelectProps) => { const [searchInput, setSearchInput] = useState(""); const [debouncedSearch, setDebouncedSearch] = useDebouncedState("", { - wait: DEBOUNCE_MS, + wait: DEBOUNCE_WAIT_MS, }); const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useInfiniteModelInfo( diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index cae6dc54df5..697318af62f 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -4,6 +4,7 @@ import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrgan import { useAllTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline"; import { ColumnDef, @@ -64,7 +65,7 @@ export function VirtualKeysTable() { pageSize: 50, }); const [filters, setFilters] = useState(DEFAULT_KEY_FILTERS); - const [debouncedFilters] = useDebouncedValue(filters, { wait: 300 }); + const [debouncedFilters] = useDebouncedValue(filters, { wait: DEBOUNCE_WAIT_MS }); const sortBy = sorting.length > 0 ? sorting[0].id : null; const sortOrder = sorting.length > 0 ? (sorting[0].desc ? "desc" : "asc") : null; diff --git a/ui/litellm-dashboard/src/components/common_components/team_dropdown.tsx b/ui/litellm-dashboard/src/components/common_components/team_dropdown.tsx index 84140135db7..7d27886c7f5 100644 --- a/ui/litellm-dashboard/src/components/common_components/team_dropdown.tsx +++ b/ui/litellm-dashboard/src/components/common_components/team_dropdown.tsx @@ -3,6 +3,7 @@ import { Select, Typography } from "antd"; import { LoadingOutlined } from "@ant-design/icons"; import { useDebouncedState } from "@tanstack/react-pacer/debouncer"; import { useInfiniteTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { Team } from "../key_team_helpers/key_list"; const { Text } = Typography; @@ -19,7 +20,6 @@ interface TeamDropdownProps { } const SCROLL_THRESHOLD = 0.8; -const DEBOUNCE_MS = 300; const TeamDropdown: React.FC = ({ value, @@ -31,7 +31,7 @@ const TeamDropdown: React.FC = ({ }) => { const [searchInput, setSearchInput] = useState(""); const [debouncedSearch, setDebouncedSearch] = useDebouncedState("", { - wait: DEBOUNCE_MS, + wait: DEBOUNCE_WAIT_MS, }); const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useInfiniteTeams( diff --git a/ui/litellm-dashboard/src/components/common_components/team_multi_select.tsx b/ui/litellm-dashboard/src/components/common_components/team_multi_select.tsx index d91f83c589b..3a48b5f7b50 100644 --- a/ui/litellm-dashboard/src/components/common_components/team_multi_select.tsx +++ b/ui/litellm-dashboard/src/components/common_components/team_multi_select.tsx @@ -3,6 +3,7 @@ import { Select, Typography } from "antd"; import { LoadingOutlined } from "@ant-design/icons"; import { useDebouncedState } from "@tanstack/react-pacer/debouncer"; import { useInfiniteTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { Team } from "../key_team_helpers/key_list"; const { Text } = Typography; @@ -17,7 +18,6 @@ interface TeamMultiSelectProps { } const SCROLL_THRESHOLD = 0.8; -const DEBOUNCE_MS = 300; const TeamMultiSelect: React.FC = ({ value = [], @@ -29,7 +29,7 @@ const TeamMultiSelect: React.FC = ({ }) => { const [searchInput, setSearchInput] = useState(""); const [debouncedSearch, setDebouncedSearch] = useDebouncedState("", { - wait: DEBOUNCE_MS, + wait: DEBOUNCE_WAIT_MS, }); const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useInfiniteTeams( diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index 207e6f2ccfe..eeccc7482e3 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -9,6 +9,7 @@ import { DataTableToolbar, } from "@/components/shared/DataTable"; import { Input } from "@/components/ui/input"; +import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; import { ColumnDef, ColumnFiltersState, OnChangeFn, PaginationState, SortingState } from "@tanstack/react-table"; @@ -43,7 +44,7 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi const [columnFilters, setColumnFilters] = useState([]); const [filtersOpen, setFiltersOpen] = useState(false); const [searchInput, setSearchInput] = useState(""); - const [searchQuery] = useDebouncedValue(searchInput, { wait: 300 }); + const [searchQuery] = useDebouncedValue(searchInput, { wait: DEBOUNCE_WAIT_MS }); const handleSearchChange = useCallback((value: string) => { setSearchInput(value); diff --git a/ui/litellm-dashboard/src/utils/debounceConstants.ts b/ui/litellm-dashboard/src/utils/debounceConstants.ts new file mode 100644 index 00000000000..bb8a1e4014c --- /dev/null +++ b/ui/litellm-dashboard/src/utils/debounceConstants.ts @@ -0,0 +1 @@ +export const DEBOUNCE_WAIT_MS = 300; From fa09cde3c09b68354e7f11b4654d15ff77f088cc Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 13 Jul 2026 12:49:01 -0700 Subject: [PATCH 327/399] feat(ui): rebuild the Virtual Keys table on the shared DataTable (#32991) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(ui): rebuild the Virtual Keys table on the shared DataTable Replaces the hand-rolled Tremor table and bespoke toolbar/pagination on the admin Virtual Keys page with the shared DataTable: server-side sort, paginate, and filter, a sticky scrolling body, a search plus column-visibility plus filters toolbar, a right-side filter drawer, and a rows-per-page footer. A page header with the existing key icon carries the Create New Key action. Adds reusable, shadcn-default building blocks for the tables migrating onto the DataTable next: shared IdentityCell, ModelsCell, and SpendBudgetCell in shared/table_cells, plus a shared PageHeader. The models cell reveals overflow in a hover tooltip and the spend/budget cell uses the Meter primitive. All data and domain logic is preserved, including the useKeys query, team and org alias resolution, the user popover, and the KeyInfoView detail swap. The rich async Team/Org/Alias filters move into the drawer, and the toolbar search maps to the key-alias substring search. Status now also reflects key expiry alongside blocked and SCIM-blocked. The VirtualKeysTable tests are updated to the new markup and extended with focused coverage for each new shared cell * fix(ui): address Virtual Keys redesign review feedback Fold the status badge into the clickable Key cell and drop the separate Status column so a key's alias, secret, and status read as one unit. The Key cell is now the single click target that opens the key detail; the whole-row click is removed Migrate the filter drawer off AntD to shadcn. A new Combobox composed from Popover and Input backs the Team, Organization, and Key Alias filters, keeping search and the alias infinite-scroll Show $0.00 for zero spend instead of a hyphen, and extend the shared DataTable with badge, chips, and meter skeleton shapes so the loading state matches the loaded cells (status pill, model chips, spend meter) rather than uniform bars Fix key sorting: the Key column sent its column id "key" as sort_by, which /key/list rejects with 400. It now sorts by the backend field key_alias * fix(ui): use the shadcn base combobox and refine the keys filters and skeletons Replace the hand-rolled filter combobox with the supported shadcn Base UI combobox (ui/combobox, added via the CLI and reused through a small SearchSelect wrapper). Its vended input-group and textarea deps are written for React 19 (plain functions with ref-as-prop); this app is on React 18, where those subcomponents drop the refs Base UI passes for focus and anchoring, so InputGroupInput, InputGroupButton, and ComboboxTrigger are adapted to forwardRef. Those ui/ files now diverge from the registry, and a future shadcn add would overwrite the adaptation until the app moves to React 19. Adds class-variance-authority, which input-group needs Give loading skeletons a per-column renderSkeleton escape hatch on the shared DataTable and mirror the Key cell exactly (alias line, secret, status pill), so skeleton rows match the real rows instead of being shorter and simpler Resolve the automated review: the toolbar search and the drawer Key Alias filter both mapped to the key-alias query, so the search silently overrode the drawer value while its chip stayed visible. Consolidate to a single alias search in the toolbar (placeholder now "Search by key alias…") and drop the redundant drawer field. Re-add coverage for the Created By column's alias-over-email display Refine the Team and Organization filters: they match on name and id, so the labels read "Team" and "Organization" rather than "... ID", each option shows the name with the id on a muted second line instead of "name (id)", and the active-filter chip shows the friendly name * chore(ui): drop duplicate class-variance-authority, use the repo cva package in input-group --- ui/litellm-dashboard/eslint-suppressions.json | 8 - .../VirtualKeysPage/VirtualKeysTable.test.tsx | 205 ++-- .../VirtualKeysPage/VirtualKeysTable.tsx | 970 ++++-------------- .../VirtualKeysPage/keyTableColumns.tsx | 353 +++++++ .../shared/DataTable/DataTable.test.tsx | 32 + .../components/shared/DataTable/DataTable.tsx | 26 +- .../components/shared/DataTable/columnMeta.ts | 3 + .../src/components/shared/DataTable/types.ts | 2 +- .../src/components/shared/PageHeader.test.tsx | 31 + .../src/components/shared/PageHeader.tsx | 29 + .../components/shared/SearchSelect.test.tsx | 64 ++ .../src/components/shared/SearchSelect.tsx | 76 ++ .../shared/table_cells/identity_cell.test.tsx | 38 + .../shared/table_cells/identity_cell.tsx | 48 + .../components/shared/table_cells/index.ts | 3 + .../shared/table_cells/models_cell.test.tsx | 45 + .../shared/table_cells/models_cell.tsx | 56 + .../table_cells/spend_budget_cell.test.tsx | 53 + .../shared/table_cells/spend_budget_cell.tsx | 44 + .../src/components/ui/combobox.tsx | 266 +++++ .../src/components/ui/input-group.tsx | 140 +++ .../src/components/ui/textarea.tsx | 18 + .../src/components/user_dashboard.tsx | 27 +- 23 files changed, 1647 insertions(+), 890 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/VirtualKeysPage/keyTableColumns.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/PageHeader.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/SearchSelect.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/SearchSelect.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/models_cell.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/models_cell.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.tsx create mode 100644 ui/litellm-dashboard/src/components/ui/combobox.tsx create mode 100644 ui/litellm-dashboard/src/components/ui/input-group.tsx create mode 100644 ui/litellm-dashboard/src/components/ui/textarea.tsx diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 953de4fe480..7c6df4e7bb9 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1690,14 +1690,6 @@ "count": 1 } }, - "src/components/VirtualKeysPage/VirtualKeysTable.tsx": { - "no-nested-ternary": { - "count": 2 - }, - "no-restricted-imports": { - "count": 1 - } - }, "src/components/activity_metrics.tsx": { "no-nested-ternary": { "count": 1 diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx index 02f5d588149..cf4beeed40f 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx @@ -63,7 +63,7 @@ const mockKey: KeyResponse = { key_alias: "Test Key Alias", spend: 5.5, max_budget: 100, - expires: "2024-12-31T23:59:59Z", + expires: "2999-12-31T23:59:59Z", models: ["gpt-3.5-turbo", "gpt-4"], aliases: {}, config: {}, @@ -154,6 +154,8 @@ const keysResult = (keys: KeyResponse[], data: Partial = {}, extra ...extra, }) as any; +const openFilters = () => fireEvent.click(screen.getByRole("button", { name: "Filters" })); + beforeEach(() => { vi.clearAllMocks(); @@ -170,6 +172,12 @@ it("should render VirtualKeysTable component", () => { expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); }); +it("renders the page header with the create-key action slot", () => { + renderWithProviders(Create New Key} />); + expect(screen.getByRole("heading", { name: "Virtual Keys" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Create New Key" })).toBeInTheDocument(); +}); + it("should display key information correctly", async () => { renderWithProviders(); @@ -177,6 +185,7 @@ it("should display key information correctly", async () => { expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); expect(screen.getByText("Test Team")).toBeInTheDocument(); expect(screen.getByText("$5.5000")).toBeInTheDocument(); + expect(screen.getByText("of $100")).toBeInTheDocument(); }); }); @@ -188,14 +197,49 @@ it("should display user email correctly", async () => { }); }); -it("should show loading message only on initial load (isPending)", () => { +it("shows the user alias over the email in the visible cell when both exist", async () => { + mockUseKeys.mockReturnValue( + keysResult([{ ...mockKey, user: { user_id: "user-1", user_email: "user@example.com", user_alias: "The User" } }]), + ); + + renderWithProviders(); + + const row = (await screen.findByText("Test Key Alias")).closest("tr") as HTMLElement; + expect(within(row).getByText("The User")).toBeInTheDocument(); + expect(within(row).queryByText("user@example.com")).not.toBeInTheDocument(); +}); + +it("shows created_by_user alias over email in the Created By column when it is enabled", async () => { + mockUseKeys.mockReturnValue( + keysResult([ + { + ...mockKey, + created_by: "some-uuid", + created_by_user: { user_id: "some-uuid", user_email: "creator@example.com", user_alias: "The Creator" }, + }, + ]), + ); + const user = userEvent.setup(); + renderWithProviders(); + + // Created By is hidden by default; turn it on via the Columns menu. + await user.click(screen.getByRole("button", { name: "Columns" })); + await user.click(await screen.findByText("Created By")); + await user.keyboard("{Escape}"); + + const row = (await screen.findByText("Test Key Alias")).closest("tr") as HTMLElement; + expect(within(row).getByText("The Creator")).toBeInTheDocument(); + expect(within(row).queryByText("creator@example.com")).not.toBeInTheDocument(); +}); + +it("should show a loading state on the initial load and hide the data", () => { mockUseKeys.mockReturnValue(keysResult([], {}, { data: null, isPending: true, isFetching: true })); renderWithProviders(); - expect(screen.getByText("🚅 Loading keys...")).toBeInTheDocument(); + expect(screen.getByText("Loading keys...")).toBeInTheDocument(); + expect(screen.getAllByTestId("skeleton-row").length).toBeGreaterThan(0); expect(screen.queryByText("Test Key Alias")).not.toBeInTheDocument(); - expect(screen.queryByText("Test Team")).not.toBeInTheDocument(); }); it("should show 'No keys found' message when the key list is empty", () => { @@ -206,61 +250,52 @@ it("should show 'No keys found' message when the key list is empty", () => { expect(screen.getByText("No keys found")).toBeInTheDocument(); }); -it("should handle models with more than 3 entries to trigger expansion UI", () => { +it("collapses models beyond the visible limit into a '+N more' badge", () => { mockUseKeys.mockReturnValue( keysResult([{ ...mockKey, models: ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo", "claude-3", "claude-3-5-sonnet"] }]), ); renderWithProviders(); - expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); + expect(screen.getByText("+2 more")).toBeInTheDocument(); }); -it("should render table headers correctly", () => { +it("should render the redesigned table headers", () => { renderWithProviders(); - expect(screen.getByText("Key ID")).toBeInTheDocument(); - expect(screen.getByText("Key Alias")).toBeInTheDocument(); + expect(screen.getByText("Key")).toBeInTheDocument(); expect(screen.getByText("Team")).toBeInTheDocument(); expect(screen.getByText("Models")).toBeInTheDocument(); - expect(screen.getByText("Spend (USD)")).toBeInTheDocument(); + expect(screen.getByText("Spend / Budget")).toBeInTheDocument(); }); -it("should handle column resizing hover events", () => { +it("sorts by the backend key_alias field (not the column label) when the Key header is clicked", async () => { renderWithProviders(); - const headerCell = document.querySelector("[data-header-id]") as HTMLElement; - expect(headerCell).toBeInTheDocument(); + const keyHeader = screen.getByText("Key").closest("button") as HTMLElement; + fireEvent.click(keyHeader); - const resizer = headerCell?.querySelector(".resizer") as HTMLElement; - expect(resizer).toBeInTheDocument(); - expect(resizer.style.opacity).toBe("0"); - - fireEvent.mouseEnter(headerCell); - expect(resizer.style.opacity).toBe("0.5"); - - fireEvent.mouseLeave(headerCell); - expect(resizer.style.opacity).toBe("0"); + await waitFor(() => { + expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ sortBy: "key_alias" })); + }); }); -it("should open KeyInfoView when clicking on a key ID button", async () => { +it("should open KeyInfoView when clicking the key cell", async () => { renderWithProviders(); await waitFor(() => { expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); }); - expect(screen.getByText(/Showing.*results/)).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toBeInTheDocument(); - const keyIdButton = screen.getByText("sk-1234567890abcdef"); - fireEvent.click(keyIdButton); + fireEvent.click(screen.getByText("Test Key Alias")); await waitFor(() => { expect(screen.getByText("Back to Keys")).toBeInTheDocument(); - expect(screen.getByText("Created At")).toBeInTheDocument(); }); - expect(screen.queryByText(/Showing.*results/)).not.toBeInTheDocument(); + expect(screen.queryByTestId("pagination-range")).not.toBeInTheDocument(); }); it("should display 'Default Proxy Admin' for user_id when value is 'default_user_id'", async () => { @@ -282,44 +317,6 @@ it("should display 'Default Proxy Admin' for user_id when value is 'default_user }); }); -it("should display created_by_user email in 'Created By' column when available", async () => { - mockUseKeys.mockReturnValue( - keysResult([ - { - ...mockKey, - created_by: "some-uuid-1234", - created_by_user: { user_id: "some-uuid-1234", user_email: "creator@example.com", user_alias: null }, - }, - ]), - ); - - renderWithProviders(); - - await waitFor(() => { - expect(screen.getByText("creator@example.com")).toBeInTheDocument(); - }); -}); - -it("should display created_by_user alias over email when both are available", async () => { - mockUseKeys.mockReturnValue( - keysResult([ - { - ...mockKey, - created_by: "some-uuid-1234", - created_by_user: { user_id: "some-uuid-1234", user_email: "creator@example.com", user_alias: "The Creator" }, - }, - ]), - ); - - renderWithProviders(); - - // Scope to the key's row so we assert the visible cell value: the hover popover that - // also holds the email is portaled out of the row, not the displayed "Created By" text. - const row = (await screen.findByText("Test Key Alias")).closest("tr") as HTMLElement; - expect(within(row).getByText("The Creator")).toBeInTheDocument(); - expect(within(row).queryByText("creator@example.com")).not.toBeInTheDocument(); -}); - it("should render table without crashing when models is null", async () => { mockUseKeys.mockReturnValue(keysResult([{ ...mockKey, models: null as unknown as string[] }])); @@ -327,6 +324,7 @@ it("should render table without crashing when models is null", async () => { await waitFor(() => { expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); + expect(screen.getByText("All Proxy Models")).toBeInTheDocument(); }); }); @@ -341,13 +339,14 @@ it("should display 'Unknown' for last_active when value is null", async () => { }); describe("server-side filtering – the LIT-4080 regression guard", () => { - it("threads an active User ID filter into the useKeys query so any refetch keeps it", async () => { + it("threads an applied User ID filter into the useKeys query so any refetch keeps it", async () => { renderWithProviders(); - fireEvent.click(screen.getByRole("button", { name: "Filters" })); + openFilters(); - const userIdInput = await screen.findByPlaceholderText("Enter User ID..."); + const userIdInput = await screen.findByPlaceholderText(/Enter User ID/); fireEvent.change(userIdInput, { target: { value: "user-42" } }); + fireEvent.click(screen.getByTestId("filter-drawer-apply")); await waitFor(() => { expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ userID: "user-42" })); @@ -361,18 +360,19 @@ describe("server-side filtering – the LIT-4080 regression guard", () => { expect(lastCall[2] ?? {}).toMatchObject({ userID: undefined, teamID: undefined, keyHash: undefined }); }); - it("drops the filter from the useKeys query when Reset Filters is clicked", async () => { + it("drops the filter from the useKeys query when it is cleared", async () => { renderWithProviders(); - fireEvent.click(screen.getByRole("button", { name: "Filters" })); - const userIdInput = await screen.findByPlaceholderText("Enter User ID..."); + openFilters(); + const userIdInput = await screen.findByPlaceholderText(/Enter User ID/); fireEvent.change(userIdInput, { target: { value: "user-42" } }); + fireEvent.click(screen.getByTestId("filter-drawer-apply")); await waitFor(() => { expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ userID: "user-42" })); }); - fireEvent.click(screen.getByRole("button", { name: "Reset Filters" })); + fireEvent.click(screen.getByTestId("datatable-clear-filters")); await waitFor(() => { const lastCall = mockUseKeys.mock.calls[mockUseKeys.mock.calls.length - 1]; @@ -388,8 +388,8 @@ describe("pagination display – total count comes from useKeys", () => { renderWithProviders(); await waitFor(() => { - expect(screen.getByText("Showing 1 - 50 of 509 results")).toBeInTheDocument(); - expect(screen.getByText("Page 1 of 11")).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-50 of 509"); + expect(screen.getByTestId("pagination-page")).toHaveTextContent("Page 1 of 11"); }); }); @@ -399,57 +399,44 @@ describe("pagination display – total count comes from useKeys", () => { renderWithProviders(); await waitFor(() => { - expect(screen.getByText("Showing 1 - 1 of 1 results")).toBeInTheDocument(); - expect(screen.getByText("Page 1 of 1")).toBeInTheDocument(); + expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-1 of 1"); + expect(screen.getByTestId("pagination-page")).toHaveTextContent("Page 1 of 1"); }); }); }); -describe("refetch button", () => { - it("should show Fetch button in normal state", () => { +describe("refresh button", () => { + it("renders an enabled refresh control in the normal state", () => { renderWithProviders(); - const fetchButton = screen.getByTitle("Fetch data"); - expect(fetchButton).toBeInTheDocument(); - expect(fetchButton).not.toBeDisabled(); - expect(screen.getByText("Fetch")).toBeInTheDocument(); + const refresh = screen.getByTestId("datatable-refresh"); + expect(refresh).toBeInTheDocument(); + expect(refresh).not.toBeDisabled(); }); - it("should show Fetching state and keep table data visible during refetch", () => { + it("disables the refresh control while a fetch is in flight but keeps data visible", () => { mockUseKeys.mockReturnValue(keysResult([mockKey], {}, { isFetching: true })); renderWithProviders(); - expect(screen.getByText("Fetching")).toBeInTheDocument(); - expect(screen.getByTitle("Fetch data")).toBeDisabled(); + expect(screen.getByTestId("datatable-refresh")).toBeDisabled(); expect(screen.getByText("Test Key Alias")).toBeInTheDocument(); - expect(screen.queryByText("🚅 Loading keys...")).not.toBeInTheDocument(); }); - it("should call refetch when Fetch button is clicked", () => { + it("calls refetch when the refresh control is clicked", () => { const mockRefetch = vi.fn(); mockUseKeys.mockReturnValue(keysResult([mockKey], {}, { refetch: mockRefetch })); renderWithProviders(); - fireEvent.click(screen.getByTitle("Fetch data")); + fireEvent.click(screen.getByTestId("datatable-refresh")); expect(mockRefetch).toHaveBeenCalledTimes(1); }); - - it("should show Fetch button enabled on error so user can retry", () => { - mockUseKeys.mockReturnValue(keysResult([], {}, { data: null, isError: true })); - - renderWithProviders(); - - const fetchButton = screen.getByTitle("Fetch data"); - expect(fetchButton).not.toBeDisabled(); - expect(screen.getByText("Fetch")).toBeInTheDocument(); - }); }); -describe("Status column reflects key.blocked / scim_blocked metadata", () => { - it("should render Active for a non-blocked key", async () => { +describe("Status column reflects blocked / expiry / scim metadata", () => { + it("renders Active for a non-blocked, unexpired key", async () => { mockUseKeys.mockReturnValue(keysResult([{ ...mockKey, blocked: false, metadata: {} }])); renderWithProviders(); @@ -459,7 +446,19 @@ describe("Status column reflects key.blocked / scim_blocked metadata", () => { }); }); - it("should render Blocked when key.blocked is true", async () => { + it("renders Expired when the expiry date has passed", async () => { + mockUseKeys.mockReturnValue( + keysResult([{ ...mockKey, blocked: false, metadata: {}, expires: "2020-01-01T00:00:00Z" }]), + ); + + renderWithProviders(); + + await waitFor(() => { + expect(screen.getByTestId(`key-status-${mockKey.token_id}`)).toHaveTextContent("Expired"); + }); + }); + + it("renders Blocked when key.blocked is true", async () => { mockUseKeys.mockReturnValue(keysResult([{ ...mockKey, blocked: true, metadata: {} }])); renderWithProviders(); @@ -470,7 +469,7 @@ describe("Status column reflects key.blocked / scim_blocked metadata", () => { expect(screen.queryByText(/Blocked by SCIM/i)).not.toBeInTheDocument(); }); - it("should mark a SCIM-blocked key with the SCIM tooltip reason", async () => { + it("marks a SCIM-blocked key with the SCIM tooltip reason", async () => { mockUseKeys.mockReturnValue(keysResult([{ ...mockKey, blocked: true, metadata: { scim_blocked: true } }])); renderWithProviders(); diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index 697318af62f..112b6cbcba4 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -1,811 +1,251 @@ "use client"; -import { useKeys, KeyListCallOptions } from "@/app/(dashboard)/hooks/keys/useKeys"; + +import { useKeys } from "@/app/(dashboard)/hooks/keys/useKeys"; import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations"; import { useAllTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; -import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; -import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline"; import { - ColumnDef, - flexRender, - getCoreRowModel, - PaginationState, - SortingState, - useReactTable, -} from "@tanstack/react-table"; -import { Badge, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react"; -import { InfoCircleOutlined, SyncOutlined } from "@ant-design/icons"; -import { Button as AntButton, Popover, Skeleton, Typography } from "antd"; -import { DateCell, IdCell, MoneyCell, StatusBadge } from "@/components/shared/table_cells"; -import React, { useDeferredValue, useMemo, useState } from "react"; -import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; -import { PaginatedKeyAliasSelect } from "../KeyAliasSelect/PaginatedKeyAliasSelect/PaginatedKeyAliasSelect"; + DataTable, + DataTableFilterDrawer, + DataTableFilterField, + DataTableToolbar, +} from "@/components/shared/DataTable"; +import { SearchSelect } from "@/components/shared/SearchSelect"; +import { PageHeader } from "@/components/shared/PageHeader"; +import { Input } from "@/components/ui/input"; +import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; +import { ColumnFiltersState, OnChangeFn, PaginationState, SortingState } from "@tanstack/react-table"; +import { KeyRound } from "lucide-react"; +import React, { useCallback, useMemo, useState } from "react"; + import { KeyResponse, Team } from "../key_team_helpers/key_list"; -import FilterComponent, { FilterOption } from "../molecules/filter"; -import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; import KeyInfoView from "../templates/key_info_view"; +import { getKeyTableColumns, KEY_TABLE_HIDDEN_COLUMNS } from "./keyTableColumns"; -type KeyFilterState = { - "Team ID": string; - "Organization ID": string; - "Key Alias": string; - "User ID": string; - "Key Hash": string; +interface VirtualKeysTableProps { + headerActions?: React.ReactNode; +} + +const DEFAULT_SORTING: SortingState = [{ id: "created_at", desc: true }]; + +const toSortOrder = (sorting: SortingState): "asc" | "desc" | undefined => { + const active = sorting[0]; + if (!active) return undefined; + return active.desc ? "desc" : "asc"; }; -const DEFAULT_KEY_FILTERS: KeyFilterState = { - "Team ID": "", - "Organization ID": "", - "Key Alias": "", - "User ID": "", - "Key Hash": "", +const FILTER_LABELS: Record = { + team_id: "Team", + org_id: "Organization", + user_id: "User ID", + key_hash: "Key ID", }; -type KeyListFilterOptions = Pick< - KeyListCallOptions, - "teamID" | "organizationID" | "selectedKeyAlias" | "userID" | "keyHash" ->; +export function VirtualKeysTable({ headerActions }: VirtualKeysTableProps) { + const { data: fetchedOrganizations } = useOrganizations(); + const organizations = useMemo(() => fetchedOrganizations ?? [], [fetchedOrganizations]); + const { data: fetchedTeams } = useAllTeams(); + const allTeams = useMemo(() => fetchedTeams ?? [], [fetchedTeams]); -const toKeyListFilters = (filters: KeyFilterState): KeyListFilterOptions => ({ - teamID: filters["Team ID"].trim() || undefined, - organizationID: filters["Organization ID"].trim() || undefined, - selectedKeyAlias: filters["Key Alias"].trim() || undefined, - userID: filters["User ID"].trim() || undefined, - keyHash: filters["Key Hash"].trim() || undefined, -}); - -export function VirtualKeysTable() { - const { data: fetchedOrganizations, isLoading: isOrgsLoading } = useOrganizations(); - const resolvedOrganizations = useMemo(() => fetchedOrganizations ?? [], [fetchedOrganizations]); const [selectedKey, setSelectedKey] = useState(null); - const [sorting, setSorting] = React.useState([{ id: "created_at", desc: true }]); - const [tablePagination, setTablePagination] = React.useState({ - pageIndex: 0, - pageSize: 50, - }); - const [filters, setFilters] = useState(DEFAULT_KEY_FILTERS); - const [debouncedFilters] = useDebouncedValue(filters, { wait: DEBOUNCE_WAIT_MS }); + const [sorting, setSorting] = useState(DEFAULT_SORTING); + const [tablePagination, setTablePagination] = useState({ pageIndex: 0, pageSize: 50 }); + const [columnFilters, setColumnFilters] = useState([]); + const [filtersOpen, setFiltersOpen] = useState(false); + const [searchInput, setSearchInput] = useState(""); + const [searchQuery] = useDebouncedValue(searchInput, { wait: DEBOUNCE_WAIT_MS }); - const sortBy = sorting.length > 0 ? sorting[0].id : null; - const sortOrder = sorting.length > 0 ? (sorting[0].desc ? "desc" : "asc") : null; + const getFilterValue = useCallback( + (columnId: string): string | undefined => { + const entry = columnFilters.find((filter) => filter.id === columnId); + return typeof entry?.value === "string" && entry.value.trim() ? entry.value.trim() : undefined; + }, + [columnFilters], + ); + + const sortBy = sorting[0]?.id; + const sortOrder = toSortOrder(sorting); + + const keyListOptions = { + teamID: getFilterValue("team_id"), + organizationID: getFilterValue("org_id"), + selectedKeyAlias: searchQuery.trim() || undefined, + userID: getFilterValue("user_id"), + keyHash: getFilterValue("key_hash"), + sortBy, + sortOrder, + expand: "user", + }; const { data: keys, isPending: isLoading, isFetching, - isError, refetch, - } = useKeys(tablePagination.pageIndex + 1, tablePagination.pageSize, { - ...toKeyListFilters(debouncedFilters), - sortBy: sortBy || undefined, - sortOrder: sortOrder || undefined, - expand: "user", - }); - const [expandedAccordions, setExpandedAccordions] = useState>({}); + } = useKeys(tablePagination.pageIndex + 1, tablePagination.pageSize, keyListOptions); const keyList = useMemo(() => keys?.keys ?? [], [keys]); + const rowCount = keys?.total_count ?? 0; - const { data: fetchedTeams, isLoading: isTeamsLoading } = useAllTeams(); - const allTeams = useMemo(() => fetchedTeams ?? [], [fetchedTeams]); - - // Defer the transition so the button stays in loading state until the table - // has rendered with the new data (mirrors the spend-logs pattern) - const isFetchingDeferred = useDeferredValue(isFetching); - const isButtonLoading = (isFetching || isFetchingDeferred) && !isError; - - const handleRefresh = () => { - refetch(); - }; - - const handleFilterChange = (newFilters: Record) => { - setFilters({ - "Team ID": newFilters["Team ID"] || "", - "Organization ID": newFilters["Organization ID"] || "", - "Key Alias": newFilters["Key Alias"] || "", - "User ID": newFilters["User ID"] || "", - "Key Hash": newFilters["Key Hash"] || "", - }); + const handleSearchChange = useCallback((value: string) => { + setSearchInput(value); setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); - }; + }, []); - const handleFilterReset = () => { - setFilters(DEFAULT_KEY_FILTERS); + const handleSortingChange = useCallback>((updaterOrValue) => { + setSorting(updaterOrValue); setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); - }; + }, []); - const totalCount = keys?.total_count ?? 0; + const handleColumnFiltersChange = useCallback>((updaterOrValue) => { + setColumnFilters(updaterOrValue); + setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); + }, []); - const columns: ColumnDef[] = useMemo( - () => [ - { - id: "expander", - header: () => null, - size: 40, - enableSorting: false, - cell: ({ row }) => - row.getCanExpand() ? ( - - ) : null, - }, - { - id: "token", - accessorKey: "token", - header: "Key ID", - size: 100, - enableSorting: true, - cell: (info) => setSelectedKey(info.row.original)} />, - }, - { - id: "key_alias", - accessorKey: "key_alias", - header: "Key Alias", - size: 150, - enableSorting: true, - cell: (info) => { - const value = info.getValue() as string; - const width = info.cell.column.getSize(); - return ( - - {value ?? "-"} - - ); - }, - }, - { - id: "status", - header: "Status", - size: 100, - enableSorting: false, - cell: ({ row }) => { - const key = row.original; - if (key.blocked !== true) { - return ; - } - const isScimBlocked = (key.metadata as Record | null | undefined)?.scim_blocked === true; - const reason = isScimBlocked - ? "Blocked by SCIM (external identity provider deactivated or deleted the owning user)." - : "Blocked. Requests using this key will be rejected with 401."; - return ( - - ); - }, - }, - { - id: "key_name", - accessorKey: "key_name", - header: "Secret Key", - size: 120, - enableSorting: false, - cell: (info) => {info.getValue() as string}, - }, - { - id: "team_alias", - accessorKey: "team_id", - header: "Team", - size: 120, - enableSorting: false, - cell: (info) => { - const teamId = info.getValue() as string | null; - if (!teamId) return "-"; - const team = allTeams.find((t) => t.team_id === teamId); - const displayValue = team?.team_alias || teamId; - const width = info.cell.column.getSize(); - return ( - - {displayValue} - - ); - }, - }, - { - id: "organization_alias", - accessorKey: "org_id", - header: "Organization", - size: 140, - enableSorting: false, - cell: (info) => { - const orgId = info.getValue() as string | null; - if (!orgId) return "-"; - const org = resolvedOrganizations.find((o) => o.organization_id === orgId); - const displayValue = org?.organization_alias || orgId; - const width = info.cell.column.getSize(); - return ( - - {displayValue} - - ); - }, - }, - { - id: "user", - accessorKey: "user", - header: () => ( - - User - - - - - ), - size: 160, - enableSorting: false, - cell: ({ row }) => { - const key = row.original; - const userAlias = key.user?.user_alias ?? null; - const userEmail = key.user?.user_email ?? key.user_email ?? null; - const userId = key.user_id ?? null; - const isDefaultAdmin = userId === "default_user_id"; - const displayValue = userAlias || userEmail || userId; - const width = 160; - - const popoverContent = ( -
- {[ - { label: "User Alias", value: userAlias }, - { label: "User Email", value: userEmail }, - { label: "User ID", value: userId }, - ].map(({ label, value }) => ( -
- {label} - {value ? ( - - {value} - - ) : ( - - - )} -
- ))} -
- ); - - if (isDefaultAdmin && !userAlias && !userEmail) { - return ( - - - - - - ); - } - - return ( - - - {displayValue || "-"} - - - ); - }, - }, - { - id: "created_at", - accessorKey: "created_at", - header: "Created At", - size: 120, - enableSorting: true, - cell: (info) => , - }, - { - id: "created_by", - accessorKey: "created_by", - header: "Created By", - size: 160, - enableSorting: false, - cell: (info) => { - const userId = info.getValue() as string | null; - if (!userId) return "-"; - const key = info.row.original; - const createdByUser = key.created_by_user; - const userAlias = createdByUser?.user_alias ?? null; - const userEmail = createdByUser?.user_email ?? null; - const isDefaultAdmin = userId === "default_user_id"; - const displayValue = userAlias || userEmail || userId; - const width = 160; - - const popoverContent = ( -
- {[ - { label: "User Alias", value: userAlias }, - { label: "User Email", value: userEmail }, - { label: "User ID", value: userId }, - ].map(({ label, value }) => ( -
- {label} - {value ? ( - - {value} - - ) : ( - - - )} -
- ))} -
- ); - - if (isDefaultAdmin && !userAlias && !userEmail) { - return ( - - - - - - ); - } - - return ( - - - {displayValue} - - - ); - }, - }, - { - id: "updated_at", - accessorKey: "updated_at", - header: "Updated At", - size: 120, - enableSorting: true, - cell: (info) => , - }, - { - id: "last_active", - accessorKey: "last_active", - header: () => ( - - Last Active - - - - - ), - size: 130, - enableSorting: false, - cell: (info) => , - }, - { - id: "expires", - accessorKey: "expires", - header: "Expires", - size: 120, - enableSorting: false, - cell: (info) => , - }, - { - id: "spend", - accessorKey: "spend", - header: "Spend (USD)", - size: 100, - enableSorting: true, - cell: (info) => , - }, - { - id: "max_budget", - accessorKey: "max_budget", - header: "Budget (USD)", - size: 110, - enableSorting: true, - cell: (info) => { - const maxBudget = info.getValue() as number | null; - if (maxBudget !== null) { - return `$${formatNumberWithCommas(maxBudget)}`; - } - const teamId = info.row.original.team_id; - const team = allTeams.find((t) => t.team_id === teamId); - if (team?.max_budget != null) { - return `$${formatNumberWithCommas(team.max_budget)} (Team)`; - } - return "Unlimited"; - }, - }, - { - id: "budget_reset_at", - accessorKey: "budget_reset_at", - header: "Budget Reset", - size: 130, - enableSorting: false, - cell: (info) => , - }, - { - id: "models", - accessorKey: "models", - header: "Models", - size: 200, - enableSorting: false, - cell: (info) => { - const models = info.getValue() as string[]; - return ( -
- {Array.isArray(models) ? ( -
- {models.length === 0 ? ( - - All Proxy Models - - ) : ( - <> -
- {models.length > 3 && ( -
- { - setExpandedAccordions((prev) => ({ - ...prev, - [info.row.id]: !prev[info.row.id], - })); - }} - /> -
- )} -
- {models.slice(0, 3).map((model, index) => - model === "all-proxy-models" ? ( - - All Proxy Models - - ) : ( - - - {model.length > 30 - ? `${getModelDisplayName(model).slice(0, 30)}...` - : getModelDisplayName(model)} - - - ), - )} - {models.length > 3 && !expandedAccordions[info.row.id] && ( - - - +{models.length - 3} {models.length - 3 === 1 ? "more model" : "more models"} - - - )} - {expandedAccordions[info.row.id] && ( -
- {models.slice(3).map((model, index) => - model === "all-proxy-models" ? ( - - All Proxy Models - - ) : ( - - - {model.length > 30 - ? `${getModelDisplayName(model).slice(0, 30)}...` - : getModelDisplayName(model)} - - - ), - )} -
- )} -
-
- - )} -
- ) : null} -
- ); - }, - }, - { - id: "rate_limits", - header: "Rate Limits", - size: 140, - enableSorting: false, - cell: ({ row }) => { - const key = row.original; - return ( -
-
TPM: {key.tpm_limit !== null ? key.tpm_limit : "Unlimited"}
-
RPM: {key.rpm_limit !== null ? key.rpm_limit : "Unlimited"}
-
- ); - }, - }, - ], - [allTeams, resolvedOrganizations], + const columns = useMemo( + () => getKeyTableColumns({ allTeams, organizations, onSelectKey: setSelectedKey }), + [allTeams, organizations], ); - const filterOptions: FilterOption[] = [ - { - name: "Team ID", - label: "Team ID", - isSearchable: true, - loading: isTeamsLoading, - searchFn: async (searchText: string) => { - if (!allTeams || allTeams.length === 0) return []; + const teamOptions = useMemo( + () => + allTeams.map((team) => ({ + label: team.team_alias || team.team_id, + value: team.team_id, + sublabel: team.team_alias ? team.team_id : undefined, + })), + [allTeams], + ); - const filteredTeams = allTeams.filter( - (team) => - team.team_id.toLowerCase().includes(searchText.toLowerCase()) || - (team.team_alias && team.team_alias.toLowerCase().includes(searchText.toLowerCase())), - ); + const orgOptions = useMemo( + () => + organizations + .filter((org) => org.organization_id) + .map((org) => { + const id = org.organization_id as string; + return { label: org.organization_alias || id, value: id, sublabel: org.organization_alias ? id : undefined }; + }), + [organizations], + ); - return filteredTeams.map((team) => ({ - label: `${team.team_alias || team.team_id} (${team.team_id})`, - value: team.team_id, - })); - }, + const formatFilterValue = useCallback( + (columnId: string, value: unknown): string => { + const raw = String(value); + if (columnId === "team_id") { + return allTeams.find((team) => team.team_id === raw)?.team_alias || raw; + } + if (columnId === "org_id") { + return organizations.find((org) => org.organization_id === raw)?.organization_alias || raw; + } + return raw; }, - { - name: "Organization ID", - label: "Organization ID", - isSearchable: true, - loading: isOrgsLoading, - searchFn: async (searchText: string) => { - if (!resolvedOrganizations || resolvedOrganizations.length === 0) return []; + [allTeams, organizations], + ); - const filteredOrgs = resolvedOrganizations.filter( - (org) => org.organization_id?.toLowerCase().includes(searchText.toLowerCase()) ?? false, - ); - - return filteredOrgs - .filter((org) => org.organization_id !== null && org.organization_id !== undefined) - .map((org) => ({ - label: `${org.organization_id || "Unknown"} (${org.organization_id})`, - value: org.organization_id as string, - })); - }, - }, - { - name: "Key Alias", - label: "Key Alias", - customComponent: PaginatedKeyAliasSelect, - }, - { - name: "User ID", - label: "User ID", - isSearchable: false, - }, - { - name: "Key Hash", - label: "Key ID", - isSearchable: false, - }, - ]; - - const table = useReactTable({ - data: keyList, - columns: columns.filter((col) => col.id !== "expander"), - columnResizeMode: "onChange", - columnResizeDirection: "ltr", - state: { - sorting, - pagination: tablePagination, - }, - onSortingChange: (updaterOrValue) => { - const newSorting = typeof updaterOrValue === "function" ? updaterOrValue(sorting) : updaterOrValue; - setSorting(newSorting); - setTablePagination((prev) => ({ ...prev, pageIndex: 0 })); - }, - onPaginationChange: setTablePagination, - getCoreRowModel: getCoreRowModel(), - enableSorting: true, - manualSorting: true, - manualPagination: true, - pageCount: Math.ceil(totalCount / tablePagination.pageSize), - }); - - const { pageIndex, pageSize } = table.getState().pagination; - const start = pageIndex * pageSize + 1; - const end = Math.min((pageIndex + 1) * pageSize, totalCount); - const rangeLabel = `${start} - ${end}`; - return ( -
- {selectedKey ? ( + if (selectedKey) { + return ( +
setSelectedKey(null)} keyData={selectedKey} teams={allTeams} + onDelete={refetch} /> - ) : ( -
-
- + ); + } + + return ( +
+ } + title="Virtual Keys" + subtitle="Every key that authenticates requests to the gateway." + actions={headerActions} + /> + row.token} + defaultColumnVisibility={KEY_TABLE_HIDDEN_COLUMNS} + sortingMode="server" + sorting={sorting} + onSortingChange={handleSortingChange} + paginationMode="server" + pagination={tablePagination} + onPaginationChange={setTablePagination} + rowCount={rowCount} + filterMode="server" + columnFilters={columnFilters} + onColumnFiltersChange={handleColumnFiltersChange} + enableColumnResizing + columnResizeMode="onChange" + isLoading={isLoading} + loadingMessage="Loading keys..." + noDataMessage="No keys found" + maxBodyHeight="calc(75vh - 210px)" + size="compact" + toolbar={(table) => ( + <> + refetch?.()} + isRefreshing={isFetching} + onOpenFilters={() => setFiltersOpen(true)} + filterLabels={FILTER_LABELS} + formatFilterValue={formatFilterValue} /> -
- -
-
- {isLoading ? ( - - ) : ( - - Showing {rangeLabel} of {totalCount} results - + + {({ get, set }) => ( + <> + + set("team_id", value)} + placeholder="Select a team…" + emptyText="No teams found" + /> + + + set("org_id", value)} + placeholder="Select an organization…" + emptyText="No organizations found" + /> + + + set("user_id", event.target.value)} + placeholder="Enter User ID…" + /> + + + set("key_hash", event.target.value)} + placeholder="Enter Key ID…" + /> + + )} - - } - onClick={handleRefresh} - disabled={isButtonLoading} - title="Fetch data" - > - {isButtonLoading ? "Fetching" : "Fetch"} - -
- -
- {isLoading ? ( - - ) : ( - - Page {pageIndex + 1} of {table.getPageCount()} - - )} - - {isLoading ? ( - - ) : ( - - )} - - {isLoading ? ( - - ) : ( - - )} -
-
-
-
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - { - const resizer = document.querySelector(`[data-header-id="${header.id}"] .resizer`); - if (resizer) { - (resizer as HTMLElement).style.opacity = "0.5"; - } - }} - onMouseLeave={() => { - const resizer = document.querySelector(`[data-header-id="${header.id}"] .resizer`); - if (resizer && !header.column.getIsResizing()) { - (resizer as HTMLElement).style.opacity = "0"; - } - }} - onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined} - > -
-
- {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} -
- {header.id !== "actions" && header.column.getCanSort() && ( -
- {header.column.getIsSorted() ? ( - { - asc: , - desc: , - }[header.column.getIsSorted() as string] - ) : ( - - )} -
- )} -
header.column.resetSize()} - onMouseDown={header.getResizeHandler()} - onTouchStart={header.getResizeHandler()} - className={`resizer ${table.options.columnResizeDirection} ${header.column.getIsResizing() ? "isResizing" : ""}`} - style={{ - position: "absolute", - right: 0, - top: 0, - height: "100%", - width: "5px", - background: header.column.getIsResizing() ? "#3b82f6" : "transparent", - cursor: "col-resize", - userSelect: "none", - touchAction: "none", - opacity: header.column.getIsResizing() ? 1 : 0, - }} - /> -
- - ))} - - ))} - - - {isLoading ? ( - - -
-

🚅 Loading keys...

-
-
-
- ) : keyList.length > 0 ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - 3 ? "px-0" : ""}`} - > - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - -
-

No keys found

-
-
-
- )} -
-
-
-
-
-
- )} + + + )} + />
); } diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/keyTableColumns.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/keyTableColumns.tsx new file mode 100644 index 00000000000..e2dc48fed9d --- /dev/null +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/keyTableColumns.tsx @@ -0,0 +1,353 @@ +"use client"; + +import { InfoCircleOutlined } from "@ant-design/icons"; +import { ColumnDef } from "@tanstack/react-table"; +import { Popover, Typography } from "antd"; + +import { DataTableSortHeader } from "@/components/shared/DataTable"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + DateCell, + IdCell, + IdentityCell, + ModelsCell, + SpendBudgetCell, + StatusBadge, + type StatusTone, +} from "@/components/shared/table_cells"; + +import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; +import { KeyResponse, Team } from "../key_team_helpers/key_list"; +import { Organization } from "../networking"; + +interface KeyStatus { + tone: StatusTone; + label: string; + tooltip?: string; +} + +const getKeyStatus = (key: KeyResponse): KeyStatus => { + if (key.blocked === true) { + const isScimBlocked = (key.metadata as Record | null | undefined)?.scim_blocked === true; + return { + tone: "error", + label: "Blocked", + tooltip: isScimBlocked + ? "Blocked by SCIM (external identity provider deactivated or deleted the owning user)." + : "Blocked. Requests using this key will be rejected with 401.", + }; + } + const expiresAt = key.expires ? Date.parse(key.expires) : Number.NaN; + if (!Number.isNaN(expiresAt) && expiresAt < Date.now()) { + return { tone: "warning", label: "Expired", tooltip: "This key has passed its expiry date." }; + } + return { tone: "success", label: "Active" }; +}; + +const UserPopoverCell = ({ + userAlias, + userEmail, + userId, + width, +}: { + userAlias: string | null; + userEmail: string | null; + userId: string | null; + width: number; +}) => { + const displayValue = userAlias || userEmail || userId; + const isDefaultAdmin = userId === "default_user_id"; + + const popoverContent = ( +
+ {[ + { label: "User Alias", value: userAlias }, + { label: "User Email", value: userEmail }, + { label: "User ID", value: userId }, + ].map(({ label, value }) => ( +
+ {label} + {value ? ( + + {value} + + ) : ( + - + )} +
+ ))} +
+ ); + + if (isDefaultAdmin && !userAlias && !userEmail) { + return ( + + + + + + ); + } + + return ( + + + {displayValue || "-"} + + + ); +}; + +const InfoHeader = ({ label, tooltip }: { label: string; tooltip: string }) => ( + + {label} + + + + +); + +interface KeyTableColumnsDeps { + allTeams: Team[]; + organizations: Organization[]; + onSelectKey: (key: KeyResponse) => void; +} + +export const getKeyTableColumns = ({ + allTeams, + organizations, + onSelectKey, +}: KeyTableColumnsDeps): ColumnDef[] => [ + { + id: "key_alias", + accessorKey: "key_alias", + meta: { + title: "Key", + renderSkeleton: () => ( +
+ +
+ + +
+
+ ), + }, + header: ({ column }) => , + size: 260, + enableSorting: true, + cell: ({ row }) => { + const status = getKeyStatus(row.original); + return ( + + } + onClick={() => onSelectKey(row.original)} + /> + ); + }, + }, + { + id: "token", + accessorKey: "token", + meta: { title: "Key ID" }, + header: ({ column }) => , + size: 120, + enableSorting: true, + cell: (info) => onSelectKey(info.row.original)} />, + }, + { + id: "team_alias", + accessorKey: "team_id", + meta: { title: "Team" }, + header: "Team", + size: 120, + enableSorting: false, + cell: (info) => { + const teamId = info.getValue() as string | null; + if (!teamId) return "-"; + const team = allTeams.find((t) => t.team_id === teamId); + const displayValue = team?.team_alias || teamId; + const width = info.cell.column.getSize(); + return ( + + {displayValue} + + ); + }, + }, + { + id: "organization_alias", + accessorKey: "org_id", + meta: { title: "Organization" }, + header: "Organization", + size: 140, + enableSorting: false, + cell: (info) => { + const orgId = info.getValue() as string | null; + if (!orgId) return "-"; + const org = organizations.find((o) => o.organization_id === orgId); + const displayValue = org?.organization_alias || orgId; + const width = info.cell.column.getSize(); + return ( + + {displayValue} + + ); + }, + }, + { + id: "user", + accessorKey: "user", + meta: { title: "User" }, + header: () => ( + + ), + size: 160, + enableSorting: false, + cell: ({ row }) => { + const key = row.original; + return ( + + ); + }, + }, + { + id: "created_at", + accessorKey: "created_at", + meta: { title: "Created At" }, + header: ({ column }) => , + size: 120, + enableSorting: true, + cell: (info) => , + }, + { + id: "created_by", + accessorKey: "created_by", + meta: { title: "Created By" }, + header: "Created By", + size: 160, + enableSorting: false, + cell: (info) => { + const userId = info.getValue() as string | null; + if (!userId) return "-"; + const createdByUser = info.row.original.created_by_user; + return ( + + ); + }, + }, + { + id: "updated_at", + accessorKey: "updated_at", + meta: { title: "Updated At" }, + header: ({ column }) => , + size: 120, + enableSorting: true, + cell: (info) => , + }, + { + id: "last_active", + accessorKey: "last_active", + meta: { title: "Last Active" }, + header: () => ( + + ), + size: 130, + enableSorting: false, + cell: (info) => , + }, + { + id: "expires", + accessorKey: "expires", + meta: { title: "Expires" }, + header: "Expires", + size: 120, + enableSorting: false, + cell: (info) => , + }, + { + id: "spend", + accessorKey: "spend", + meta: { title: "Spend / Budget", skeleton: "meter" }, + header: ({ column }) => , + size: 180, + enableSorting: true, + cell: ({ row }) => { + const teamId = row.original.team_id; + const team = allTeams.find((t) => t.team_id === teamId); + return ( + + ); + }, + }, + { + id: "budget_reset_at", + accessorKey: "budget_reset_at", + meta: { title: "Budget Reset" }, + header: "Budget Reset", + size: 130, + enableSorting: false, + cell: (info) => , + }, + { + id: "models", + accessorKey: "models", + meta: { title: "Models", skeleton: "chips" }, + header: "Models", + size: 220, + enableSorting: false, + cell: (info) => , + }, + { + id: "rate_limits", + meta: { title: "Rate Limits" }, + header: "Rate Limits", + size: 140, + enableSorting: false, + cell: ({ row }) => { + const key = row.original; + return ( +
+
TPM: {key.tpm_limit !== null ? key.tpm_limit : "Unlimited"}
+
RPM: {key.rpm_limit !== null ? key.rpm_limit : "Unlimited"}
+
+ ); + }, + }, +]; + +export const KEY_TABLE_HIDDEN_COLUMNS: Record = { + token: false, + organization_alias: false, + created_by: false, + updated_at: false, + expires: false, + budget_reset_at: false, + rate_limits: false, +}; diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx index ef0d842ad3e..d8d4c9392dc 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx @@ -303,6 +303,38 @@ describe("DataTable loading", () => { // per-column widths differ instead of every cell sharing one fixed width expect(new Set(bars.map((bar) => bar.className)).size).toBeGreaterThan(1); }); + + it("renders shape-specific skeletons for badge, chips, and meter columns", () => { + const columns: ColumnDef[] = [ + { id: "badge", header: "Badge", meta: { skeleton: "badge" }, cell: () => null }, + { id: "chips", header: "Chips", meta: { skeleton: "chips" }, cell: () => null }, + { id: "meter", header: "Meter", meta: { skeleton: "meter" }, cell: () => null }, + ]; + render(); + + const firstRow = screen.getAllByTestId("skeleton-row").at(0); + const cells = Array.from(firstRow?.querySelectorAll("td") ?? []); + const barsIn = (cell: Element | undefined) => cell?.querySelectorAll('[data-slot="skeleton"]').length ?? 0; + + // badge = a single pill, chips = three pills, meter = value bar + track bar + expect(barsIn(cells[0])).toBe(1); + expect(cells[0]?.querySelector('[data-slot="skeleton"]')?.className).toContain("rounded-full"); + expect(barsIn(cells[1])).toBe(3); + expect(barsIn(cells[2])).toBe(2); + }); + + it("uses a column's renderSkeleton override when provided", () => { + const columns: ColumnDef[] = [ + { + id: "custom", + header: "Custom", + meta: { renderSkeleton: () =>
loading
}, + cell: () => null, + }, + ]; + render(); + expect(screen.getAllByTestId("custom-skeleton").length).toBeGreaterThan(0); + }); }); describe("DataTable column visibility", () => { diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx index 758ca5a597b..bc13318dd58 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx @@ -338,7 +338,11 @@ const SKELETON_WIDTHS = ["w-[58%]", "w-[44%]", "w-[70%]", "w-[50%]", "w-[64%]", function SkeletonCell({ column, index }: { column: Column | undefined; index: number }) { const meta = column?.columnDef.meta; const width = SKELETON_WIDTHS[index % SKELETON_WIDTHS.length]; - if (meta?.skeleton === "twoLine") { + const shape = meta?.skeleton; + if (meta?.renderSkeleton !== undefined) { + return <>{meta.renderSkeleton()}; + } + if (shape === "twoLine") { return (
@@ -346,6 +350,26 @@ function SkeletonCell({ column, index }: { column: Column
); } + if (shape === "badge") { + return ; + } + if (shape === "chips") { + return ( +
+ + + +
+ ); + } + if (shape === "meter") { + return ( +
+ + +
+ ); + } return ; } diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts b/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts index 0f14c277c6f..eff4e0cb7db 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts +++ b/ui/litellm-dashboard/src/components/shared/DataTable/columnMeta.ts @@ -1,4 +1,5 @@ import type { RowData } from "@tanstack/react-table"; +import type * as React from "react"; import type { ColumnPinnedSide, DataTableSkeletonShape } from "./types"; @@ -10,5 +11,7 @@ declare module "@tanstack/react-table" { title?: string; pinned?: ColumnPinnedSide; skeleton?: DataTableSkeletonShape; + /** Full control over this column's loading skeleton, for cells the built-in shapes can't mirror. */ + renderSkeleton?: () => React.ReactNode; } } diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/types.ts b/ui/litellm-dashboard/src/components/shared/DataTable/types.ts index f5130b4c823..672ab512ef4 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/types.ts +++ b/ui/litellm-dashboard/src/components/shared/DataTable/types.ts @@ -18,7 +18,7 @@ export type FilterMode = "none" | "client" | "server"; export type ColumnResizeMode = "onEnd" | "onChange"; export type DataTableSize = "compact" | "default"; export type ColumnPinnedSide = "left" | "right"; -export type DataTableSkeletonShape = "text" | "twoLine"; +export type DataTableSkeletonShape = "text" | "twoLine" | "badge" | "chips" | "meter"; export interface DataTableProps { data: TData[]; diff --git a/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx b/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx new file mode 100644 index 00000000000..f7a313271da --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx @@ -0,0 +1,31 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { PageHeader } from "./PageHeader"; + +describe("PageHeader", () => { + it("renders the title as a heading", () => { + render(); + expect(screen.getByRole("heading", { name: "Virtual Keys" })).toBeInTheDocument(); + }); + + it("renders the subtitle, icon, and actions when provided", () => { + render( + } + actions={} + />, + ); + expect(screen.getByText("Every key that authenticates requests")).toBeInTheDocument(); + expect(screen.getByTestId("icon")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Create New Key" })).toBeInTheDocument(); + }); + + it("omits the optional slots when not provided", () => { + render(); + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + expect(document.querySelector("p")).toBeNull(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/PageHeader.tsx b/ui/litellm-dashboard/src/components/shared/PageHeader.tsx new file mode 100644 index 00000000000..34d478e1cd9 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/PageHeader.tsx @@ -0,0 +1,29 @@ +"use client"; + +import * as React from "react"; + +interface PageHeaderProps { + title: React.ReactNode; + subtitle?: React.ReactNode; + icon?: React.ReactNode; + actions?: React.ReactNode; +} + +export function PageHeader({ title, subtitle, icon, actions }: PageHeaderProps) { + return ( +
+
+ {icon != null && ( + + {icon} + + )} +
+

{title}

+ {subtitle != null &&

{subtitle}

} +
+
+ {actions != null &&
{actions}
} +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/shared/SearchSelect.test.tsx b/ui/litellm-dashboard/src/components/shared/SearchSelect.test.tsx new file mode 100644 index 00000000000..acf50d282b4 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/SearchSelect.test.tsx @@ -0,0 +1,64 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; + +import { SearchSelect } from "./SearchSelect"; + +const OPTIONS = [ + { label: "Acme Prod", value: "team-1" }, + { label: "Growth", value: "team-2" }, + { label: "Data Team", value: "team-3" }, +]; + +describe("SearchSelect", () => { + it("renders the placeholder when nothing is selected", () => { + render(); + expect(screen.getByPlaceholderText("Select Team…")).toBeInTheDocument(); + }); + + it("shows the selected option's label in the field", () => { + render(); + expect(screen.getByRole("combobox")).toHaveValue("Growth"); + }); + + it("shows a clear control only when a value is selected", () => { + const { rerender } = render(); + expect(document.querySelector('[data-slot="combobox-clear"]')).toBeNull(); + rerender(); + expect(document.querySelector('[data-slot="combobox-clear"]')).not.toBeNull(); + }); + + it("filters the options client-side as you type", async () => { + const user = userEvent.setup(); + render(); + const input = screen.getByRole("combobox"); + await user.click(input); + await user.type(input, "grow"); + expect(await screen.findByText("Growth")).toBeInTheDocument(); + expect(screen.queryByText("Acme Prod")).not.toBeInTheDocument(); + }); + + it("renders a muted sublabel and matches it when searching", async () => { + const user = userEvent.setup(); + render( + , + ); + const input = screen.getByRole("combobox"); + await user.click(input); + expect(await screen.findByText("team-abc-123")).toBeInTheDocument(); + await user.type(input, "abc-123"); + expect(await screen.findByText("Acme Prod")).toBeInTheDocument(); + }); + + it("selects an option and reports its value", async () => { + const onValueChange = vi.fn(); + const user = userEvent.setup(); + render(); + await user.click(screen.getByRole("combobox")); + await user.click(await screen.findByText("Growth")); + expect(onValueChange).toHaveBeenCalledWith("team-2"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/SearchSelect.tsx b/ui/litellm-dashboard/src/components/shared/SearchSelect.tsx new file mode 100644 index 00000000000..c29e099a1c6 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/SearchSelect.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { + Combobox, + ComboboxContent, + ComboboxEmpty, + ComboboxInput, + ComboboxItem, + ComboboxList, +} from "@/components/ui/combobox"; + +export interface SearchSelectOption { + label: string; + value: string; + /** Optional muted second line (e.g. an id); also matched when searching. */ + sublabel?: string; +} + +interface SearchSelectProps { + options: SearchSelectOption[]; + value?: string; + onValueChange: (value: string) => void; + placeholder?: string; + emptyText?: string; + disabled?: boolean; + className?: string; +} + +export function SearchSelect({ + options, + value, + onValueChange, + placeholder = "Select…", + emptyText = "No results", + disabled = false, + className, +}: SearchSelectProps) { + const selected = options.find((option) => option.value === value) ?? null; + + return ( + onValueChange(item?.value ?? "")} + isItemEqualToValue={(a: SearchSelectOption, b: SearchSelectOption) => a.value === b.value} + itemToStringLabel={(item: SearchSelectOption) => item.label} + filter={(item: SearchSelectOption, query: string) => { + const q = query.trim().toLowerCase(); + if (!q) return true; + return item.label.toLowerCase().includes(q) || (item.sublabel?.toLowerCase().includes(q) ?? false); + }} + disabled={disabled} + > + + + {emptyText} + + {(item: SearchSelectOption) => ( + + + {item.label} + {item.sublabel != null && item.sublabel !== "" && ( + {item.sublabel} + )} + + + )} + + + + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx new file mode 100644 index 00000000000..db4e93c7cb2 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx @@ -0,0 +1,38 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; + +import { IdentityCell } from "./identity_cell"; + +describe("IdentityCell", () => { + it("renders the title", () => { + render(); + expect(screen.getByText("prod-gateway")).toBeInTheDocument(); + }); + + it("renders the subtitle and an inline badge together", () => { + render(Active} />); + expect(screen.getByText("sk-...v0Pw")).toBeInTheDocument(); + expect(screen.getByText("Active")).toBeInTheDocument(); + }); + + it("omits the subtitle row when there is no subtitle or badge", () => { + render(); + expect(document.querySelector("span.font-mono")).toBeNull(); + }); + + it("renders a static div (no button) when not clickable", () => { + render(); + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + + it("renders a clickable button and fires onClick", async () => { + const onClick = vi.fn(); + const user = userEvent.setup(); + render(); + const button = screen.getByRole("button"); + expect(button.querySelector(".lucide-chevron-right")).not.toBeNull(); + await user.click(button); + expect(onClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.tsx new file mode 100644 index 00000000000..4d3e3d8e4dd --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { ChevronRight } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/cva.config"; + +interface IdentityCellProps { + title: React.ReactNode; + subtitle?: React.ReactNode; + badge?: React.ReactNode; + onClick?: () => void; + className?: string; + titleClassName?: string; +} + +export function IdentityCell({ title, subtitle, badge, onClick, className, titleClassName }: IdentityCellProps) { + const hasSubtitleRow = (subtitle != null && subtitle !== "") || badge != null; + + const body = ( +
+ {title} + {hasSubtitleRow && ( + + {subtitle != null && subtitle !== "" && ( + {subtitle} + )} + {badge} + + )} +
+ ); + + if (onClick != null) { + return ( + + ); + } + + return
{body}
; +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/index.ts b/ui/litellm-dashboard/src/components/shared/table_cells/index.ts index e189413d43d..9fdd04d169c 100644 --- a/ui/litellm-dashboard/src/components/shared/table_cells/index.ts +++ b/ui/litellm-dashboard/src/components/shared/table_cells/index.ts @@ -1,5 +1,8 @@ export { CellTooltip } from "./cell_tooltip"; export { DateCell, formatCellDate, formatFullTimestamp, type DatePrecision } from "./date_cell"; export { IdCell, type IdCellVariant } from "./id_cell"; +export { IdentityCell } from "./identity_cell"; +export { ModelsCell } from "./models_cell"; export { MoneyCell } from "./money_cell"; +export { SpendBudgetCell } from "./spend_budget_cell"; export { StatusBadge, type StatusTone } from "./status_badge"; diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/models_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/models_cell.test.tsx new file mode 100644 index 00000000000..d3fad1d3244 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/models_cell.test.tsx @@ -0,0 +1,45 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it } from "vitest"; + +import { ModelsCell } from "./models_cell"; + +describe("ModelsCell", () => { + it("shows 'All Proxy Models' when the list is empty, null, or undefined", () => { + const { rerender } = render(); + expect(screen.getByText("All Proxy Models")).toBeInTheDocument(); + rerender(); + expect(screen.getByText("All Proxy Models")).toBeInTheDocument(); + rerender(); + expect(screen.getByText("All Proxy Models")).toBeInTheDocument(); + }); + + it("renders every model with no overflow badge when at or below the limit", () => { + render(); + expect(screen.getByText("gpt-4o")).toBeInTheDocument(); + expect(screen.getByText("claude-sonnet-4-5")).toBeInTheDocument(); + expect(screen.getByText("o3-mini")).toBeInTheDocument(); + expect(screen.queryByText(/more$/)).not.toBeInTheDocument(); + }); + + it("collapses models beyond the limit into a '+N more' badge", () => { + render(); + expect(screen.getByText("a")).toBeInTheDocument(); + expect(screen.getByText("b")).toBeInTheDocument(); + expect(screen.queryByText("c")).not.toBeInTheDocument(); + expect(screen.getByText("+3 more")).toBeInTheDocument(); + }); + + it("reveals the hidden models in a tooltip on hover", async () => { + const user = userEvent.setup(); + render(); + await user.hover(screen.getByText("+2 more")); + expect(await screen.findByText("c")).toBeInTheDocument(); + expect(await screen.findByText("d")).toBeInTheDocument(); + }); + + it("labels the all-proxy-models wildcard", () => { + render(); + expect(screen.getByText("All Proxy Models")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/models_cell.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/models_cell.tsx new file mode 100644 index 00000000000..712d8511c78 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/models_cell.tsx @@ -0,0 +1,56 @@ +"use client"; + +import { getModelDisplayName } from "@/components/key_team_helpers/fetch_available_models_team_key"; +import { Badge } from "@/components/ui/badge"; + +import { CellTooltip } from "./cell_tooltip"; + +interface ModelsCellProps { + models: string[] | null | undefined; + maxVisible?: number; +} + +const WILDCARD_MODEL = "all-proxy-models"; + +const formatModel = (model: string): string => { + if (model === WILDCARD_MODEL) { + return "All Proxy Models"; + } + const name = getModelDisplayName(model); + return name.length > 30 ? `${name.slice(0, 30)}...` : name; +}; + +export function ModelsCell({ models, maxVisible = 3 }: ModelsCellProps) { + if (!Array.isArray(models) || models.length === 0) { + return All Proxy Models; + } + + const visible = models.slice(0, maxVisible); + const overflow = models.slice(maxVisible); + + return ( +
+ {visible.map((model, index) => ( + + {formatModel(model)} + + ))} + {overflow.length > 0 && ( + + {overflow.map((model, index) => ( + {formatModel(model)} + ))} +
+ } + trigger={ + + +{overflow.length} more + + } + /> + )} +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.test.tsx new file mode 100644 index 00000000000..707441aef1d --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.test.tsx @@ -0,0 +1,53 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { SpendBudgetCell } from "./spend_budget_cell"; + +const indicator = (container: HTMLElement) => container.querySelector('[data-slot="meter-indicator"]'); + +describe("SpendBudgetCell", () => { + it("shows Unlimited and renders no meter when there is no budget", () => { + const { container } = render(); + expect(screen.getByText("· Unlimited")).toBeInTheDocument(); + expect(screen.queryByRole("meter")).not.toBeInTheDocument(); + expect(indicator(container)).toBeNull(); + }); + + it("shows $0.00 for zero or undefined spend, never a hyphen", () => { + const { rerender } = render(); + expect(screen.getByText("$0.00")).toBeInTheDocument(); + expect(screen.queryByText("-")).not.toBeInTheDocument(); + rerender(); + expect(screen.getByText("$0.00")).toBeInTheDocument(); + expect(screen.queryByText("-")).not.toBeInTheDocument(); + }); + + it("renders a meter carrying the spend and budget when a budget exists", () => { + render(); + const meter = screen.getByRole("meter"); + expect(meter).toHaveAttribute("aria-valuenow", "25"); + expect(meter).toHaveAttribute("aria-valuemax", "100"); + expect(screen.getByText("of $100")).toBeInTheDocument(); + }); + + it("keeps the default tone below 80% usage", () => { + const { container } = render(); + expect(indicator(container)?.className).toContain("bg-primary"); + }); + + it("switches to the warning tone at 80% usage", () => { + const { container } = render(); + expect(indicator(container)?.className).toContain("bg-amber-500"); + }); + + it("switches to the over tone above 100% usage", () => { + const { container } = render(); + expect(indicator(container)?.className).toContain("bg-destructive"); + }); + + it("falls back to the team budget and labels it", () => { + render(); + expect(screen.getByText("of $200 (Team)")).toBeInTheDocument(); + expect(screen.getByRole("meter")).toHaveAttribute("aria-valuemax", "200"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.tsx new file mode 100644 index 00000000000..10956f23b1c --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/table_cells/spend_budget_cell.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { Meter, MeterIndicator, MeterTrack } from "@/components/ui/meter"; +import { formatNumberWithCommas, getSpendString } from "@/utils/dataUtils"; + +interface SpendBudgetCellProps { + spend: number | null | undefined; + maxBudget: number | null | undefined; + teamMaxBudget?: number | null; +} + +const meterTone = (pct: number): "default" | "warning" | "over" => { + if (pct > 100) return "over"; + if (pct >= 80) return "warning"; + return "default"; +}; + +export function SpendBudgetCell({ spend, maxBudget, teamMaxBudget }: SpendBudgetCellProps) { + const spendValue = typeof spend === "number" && !Number.isNaN(spend) ? spend : 0; + const budget = maxBudget ?? teamMaxBudget ?? null; + const isTeamBudget = maxBudget == null && teamMaxBudget != null; + const hasBudget = typeof budget === "number" && budget > 0; + const pct = hasBudget ? (spendValue / budget) * 100 : 0; + + const spendText = spendValue > 0 ? getSpendString(spendValue, 4) : "$0.00"; + const budgetLabel = + budget === null ? "· Unlimited" : `of $${formatNumberWithCommas(budget)}${isTeamBudget ? " (Team)" : ""}`; + + return ( +
+
+ {spendText}{" "} + {budgetLabel} +
+ {hasBudget && ( + + + + + + )} +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/ui/combobox.tsx b/ui/litellm-dashboard/src/components/ui/combobox.tsx new file mode 100644 index 00000000000..2854928140e --- /dev/null +++ b/ui/litellm-dashboard/src/components/ui/combobox.tsx @@ -0,0 +1,266 @@ +"use client"; + +import * as React from "react"; +import { Combobox as ComboboxPrimitive } from "@base-ui/react"; + +import { cn } from "@/lib/cva.config"; +import { Button } from "@/components/ui/button"; +import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"; +import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"; + +const Combobox = ComboboxPrimitive.Root; + +function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { + return ; +} + +const ComboboxTrigger = React.forwardRef< + React.ComponentRef, + ComboboxPrimitive.Trigger.Props +>(({ className, children, ...props }, ref) => { + return ( + + {children} + + + ); +}); +ComboboxTrigger.displayName = "ComboboxTrigger"; + +function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { + return ( + } + className={cn(className)} + {...props} + > + + + ); +} + +function ComboboxInput({ + className, + children, + disabled = false, + showTrigger = true, + showClear = false, + ...props +}: ComboboxPrimitive.Input.Props & { + showTrigger?: boolean; + showClear?: boolean; +}) { + return ( + + } {...props} /> + + {showTrigger && ( + } + data-slot="input-group-button" + className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent" + disabled={disabled} + /> + )} + {showClear && } + + {children} + + ); +} + +function ComboboxContent({ + className, + side = "bottom", + sideOffset = 6, + align = "start", + alignOffset = 0, + anchor, + ...props +}: ComboboxPrimitive.Popup.Props & + Pick) { + return ( + + + + + + ); +} + +function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { + return ( + + ); +} + +function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) { + return ( + + {children} + } + > + + + + ); +} + +function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { + return ; +} + +function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) { + return ( + + ); +} + +function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { + return ; +} + +function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { + return ( + + ); +} + +function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) { + return ( + + ); +} + +function ComboboxChips({ + className, + ...props +}: React.ComponentPropsWithRef & ComboboxPrimitive.Chips.Props) { + return ( + + ); +} + +function ComboboxChip({ + className, + children, + showRemove = true, + ...props +}: ComboboxPrimitive.Chip.Props & { + showRemove?: boolean; +}) { + return ( + + {children} + {showRemove && ( + } + className="-ml-1 opacity-50 hover:opacity-100" + data-slot="combobox-chip-remove" + > + + + )} + + ); +} + +function ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props) { + return ( + + ); +} + +function useComboboxAnchor() { + return React.useRef(null); +} + +export { + Combobox, + ComboboxInput, + ComboboxContent, + ComboboxList, + ComboboxItem, + ComboboxGroup, + ComboboxLabel, + ComboboxCollection, + ComboboxEmpty, + ComboboxSeparator, + ComboboxChips, + ComboboxChip, + ComboboxChipsInput, + ComboboxTrigger, + ComboboxValue, + useComboboxAnchor, +}; diff --git a/ui/litellm-dashboard/src/components/ui/input-group.tsx b/ui/litellm-dashboard/src/components/ui/input-group.tsx new file mode 100644 index 00000000000..8ee9b7f17bd --- /dev/null +++ b/ui/litellm-dashboard/src/components/ui/input-group.tsx @@ -0,0 +1,140 @@ +"use client"; + +import * as React from "react"; +import { type VariantProps } from "cva"; + +import { cn, cva } from "@/lib/cva.config"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; + +function InputGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", + className, + )} + {...props} + /> + ); +} + +const inputGroupAddonVariants = cva({ + base: "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", + variants: { + align: { + "inline-start": "order-first pl-2 has-[>button]:-ml-1 has-[>kbd]:ml-[-0.15rem]", + "inline-end": "order-last pr-2 has-[>button]:-mr-1 has-[>kbd]:mr-[-0.15rem]", + "block-start": + "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2", + "block-end": "order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2", + }, + }, + defaultVariants: { + align: "inline-start", + }, +}); + +function InputGroupAddon({ + className, + align = "inline-start", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
{ + if ((e.target as HTMLElement).closest("button")) { + return; + } + e.currentTarget.parentElement?.querySelector("input")?.focus(); + }} + {...props} + /> + ); +} + +const inputGroupButtonVariants = cva({ + base: "flex items-center gap-2 text-sm shadow-none", + variants: { + size: { + xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5", + sm: "", + "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0", + "icon-sm": "size-8 p-0 has-[>svg]:p-0", + }, + }, + defaultVariants: { + size: "xs", + }, +}); + +const InputGroupButton = React.forwardRef< + React.ComponentRef, + Omit, "size" | "type"> & + VariantProps & { + type?: "button" | "submit" | "reset"; + } +>(({ className, type = "button", variant = "ghost", size = "xs", ...props }, ref) => { + return ( +