From 345094a49d01c02b0a3fbb7dbae20608038ed135 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 13 Jun 2024 15:54:24 -0700 Subject: [PATCH 01/24] fix(utils.py): check if model info is for model with correct provider Fixes issue where incorrect pricing was used for custom llm provider --- litellm/proxy/_experimental/out/404.html | 1 - .../proxy/_experimental/out/model_hub.html | 1 - .../proxy/_experimental/out/onboarding.html | 1 - litellm/proxy/_super_secret_config.yaml | 11 ++++++- litellm/proxy/proxy_server.py | 14 +++++---- litellm/tests/test_get_model_info.py | 14 +++++++++ litellm/utils.py | 29 ++++++++++++++----- .../src/components/model_dashboard.tsx | 2 +- 8 files changed, 55 insertions(+), 18 deletions(-) delete mode 100644 litellm/proxy/_experimental/out/404.html delete mode 100644 litellm/proxy/_experimental/out/model_hub.html delete mode 100644 litellm/proxy/_experimental/out/onboarding.html diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html deleted file mode 100644 index e97c72fc13..0000000000 --- a/litellm/proxy/_experimental/out/404.html +++ /dev/null @@ -1 +0,0 @@ -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/model_hub.html b/litellm/proxy/_experimental/out/model_hub.html deleted file mode 100644 index 5b965b3bdc..0000000000 --- a/litellm/proxy/_experimental/out/model_hub.html +++ /dev/null @@ -1 +0,0 @@ -LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/onboarding.html b/litellm/proxy/_experimental/out/onboarding.html deleted file mode 100644 index 33efe1f8ea..0000000000 --- a/litellm/proxy/_experimental/out/onboarding.html +++ /dev/null @@ -1 +0,0 @@ -LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_super_secret_config.yaml b/litellm/proxy/_super_secret_config.yaml index 2b20547565..ea679b92b7 100644 --- a/litellm/proxy/_super_secret_config.yaml +++ b/litellm/proxy/_super_secret_config.yaml @@ -55,7 +55,16 @@ model_list: model: textembedding-gecko-multilingual@001 vertex_project: my-project-9d5c vertex_location: us-central1 - +- model_name: lbl/command-r-plus + litellm_params: + model: openai/lbl/command-r-plus + api_key: "os.environ/VLLM_API_KEY" + api_base: http://vllm-command:8000/v1 + rpm: 1000 + input_cost_per_token: 0 + output_cost_per_token: 0 + model_info: + max_input_tokens: 80920 assistant_settings: custom_llm_provider: openai litellm_params: diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 3e979c0787..53feaede87 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -11402,7 +11402,7 @@ async def model_info_v2( for _model in all_models: # provided model_info in config.yaml model_info = _model.get("model_info", {}) - if debug == True: + if debug is True: _openai_client = "None" if llm_router is not None: _openai_client = ( @@ -11427,7 +11427,7 @@ async def model_info_v2( litellm_model = litellm_params.get("model", None) try: litellm_model_info = litellm.get_model_info(model=litellm_model) - except: + except Exception: litellm_model_info = {} # 3rd pass on the model, try seeing if we can find model but without the "/" in model cost map if litellm_model_info == {}: @@ -11438,8 +11438,10 @@ async def model_info_v2( if len(split_model) > 0: litellm_model = split_model[-1] try: - litellm_model_info = litellm.get_model_info(model=litellm_model) - except: + litellm_model_info = litellm.get_model_info( + model=litellm_model, custom_llm_provider=split_model[0] + ) + except Exception: litellm_model_info = {} for k, v in litellm_model_info.items(): if k not in model_info: @@ -11950,7 +11952,9 @@ async def model_info_v1( if len(split_model) > 0: litellm_model = split_model[-1] try: - litellm_model_info = litellm.get_model_info(model=litellm_model) + litellm_model_info = litellm.get_model_info( + model=litellm_model, custom_llm_provider=split_model[0] + ) except: litellm_model_info = {} for k, v in litellm_model_info.items(): diff --git a/litellm/tests/test_get_model_info.py b/litellm/tests/test_get_model_info.py index ec6843df80..3fd6a6d22f 100644 --- a/litellm/tests/test_get_model_info.py +++ b/litellm/tests/test_get_model_info.py @@ -7,6 +7,7 @@ sys.path.insert( ) # Adds the parent directory to the system path import litellm from litellm import get_model_info +import pytest def test_get_model_info_simple_model_name(): @@ -23,3 +24,16 @@ def test_get_model_info_custom_llm_with_model_name(): """ model = "anthropic/claude-3-opus-20240229" litellm.get_model_info(model) + + +def test_get_model_info_custom_llm_with_same_name_vllm(): + """ + Tests if {custom_llm_provider}/{model_name} name given, and model exists in model info, the object is returned + """ + model = "command-r-plus" + provider = "openai" # vllm is openai-compatible + try: + litellm.get_model_info(model, custom_llm_provider=provider) + pytest.fail("Expected get model info to fail for an unmapped model/provider") + except Exception: + pass diff --git a/litellm/utils.py b/litellm/utils.py index 63506a1d9d..aaf2c1b945 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -6953,13 +6953,14 @@ def get_max_tokens(model: str): ) -def get_model_info(model: str) -> ModelInfo: +def get_model_info(model: str, custom_llm_provider: Optional[str] = None) -> ModelInfo: """ Get a dict for the maximum tokens (context window), input_cost_per_token, output_cost_per_token for a given model. Parameters: - model (str): The name of the model. + - model (str): The name of the model. + - custom_llm_provider (str | null): the provider used for the model. If provided, used to check if the litellm model info is for that provider. Returns: dict: A dictionary containing the following information: @@ -7013,12 +7014,14 @@ def get_model_info(model: str) -> ModelInfo: if model in azure_llms: model = azure_llms[model] ########################## - # Get custom_llm_provider - split_model, custom_llm_provider = model, "" - try: - split_model, custom_llm_provider, _, _ = get_llm_provider(model=model) - except: - pass + if custom_llm_provider is None: + # Get custom_llm_provider + try: + split_model, custom_llm_provider, _, _ = get_llm_provider(model=model) + except: + pass + else: + split_model = model ######################### supported_openai_params = litellm.get_supported_openai_params( @@ -7043,10 +7046,20 @@ def get_model_info(model: str) -> ModelInfo: if model in litellm.model_cost: _model_info = litellm.model_cost[model] _model_info["supported_openai_params"] = supported_openai_params + if ( + "litellm_provider" in _model_info + and _model_info["litellm_provider"] != custom_llm_provider + ): + raise Exception return _model_info if split_model in litellm.model_cost: _model_info = litellm.model_cost[split_model] _model_info["supported_openai_params"] = supported_openai_params + if ( + "litellm_provider" in _model_info + and _model_info["litellm_provider"] != custom_llm_provider + ): + raise Exception return _model_info else: raise ValueError( diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index e18f4233eb..44ce40ebbb 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1531,7 +1531,7 @@ const ModelDashboard: React.FC = ({
                               {model.input_cost
                                 ? model.input_cost
-                                : model.litellm_params.input_cost_per_token
+                                : model.litellm_params.input_cost_per_token != null && model.litellm_params.input_cost_per_token != undefined
                                   ? (
                                       Number(
                                         model.litellm_params

From 51f873b54d7d1429719a002e1bdba8ffc87be83c Mon Sep 17 00:00:00 2001
From: David Manouchehri 
Date: Thu, 13 Jun 2024 20:57:51 +0000
Subject: [PATCH 02/24] fix(caching.py): Stop throwing constant spam errors on
 every single S3 cache miss. Fixes #4146.

---
 litellm/caching.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/litellm/caching.py b/litellm/caching.py
index 497dd2371c..6b58cf5276 100644
--- a/litellm/caching.py
+++ b/litellm/caching.py
@@ -1192,7 +1192,7 @@ class S3Cache(BaseCache):
             return cached_response
         except botocore.exceptions.ClientError as e:
             if e.response["Error"]["Code"] == "NoSuchKey":
-                verbose_logger.error(
+                verbose_logger.debug(
                     f"S3 Cache: The specified key '{key}' does not exist in the S3 bucket."
                 )
                 return None

From 21ed23296ffc61889d6d3e020557cc7711438d93 Mon Sep 17 00:00:00 2001
From: Ishaan Jaff 
Date: Thu, 13 Jun 2024 14:52:24 -0700
Subject: [PATCH 03/24] fix - clean up swagger spend endpoints

---
 litellm/proxy/proxy_server.py | 36 +++++++++++++++++++++++++----------
 1 file changed, 26 insertions(+), 10 deletions(-)

diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py
index 53feaede87..dd8b2d61ab 100644
--- a/litellm/proxy/proxy_server.py
+++ b/litellm/proxy/proxy_server.py
@@ -7052,6 +7052,7 @@ async def info_key_fn(
     "/spend/keys",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def spend_key_fn():
     """
@@ -7084,6 +7085,7 @@ async def spend_key_fn():
     "/spend/users",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def spend_user_fn(
     user_id: Optional[str] = fastapi.Query(
@@ -7214,6 +7216,7 @@ async def view_spend_tags(
     responses={
         200: {"model": List[LiteLLM_SpendLogs]},
     },
+    include_in_schema=False,
 )
 async def get_global_activity(
     start_date: Optional[str] = fastapi.Query(
@@ -7317,6 +7320,7 @@ async def get_global_activity(
     responses={
         200: {"model": List[LiteLLM_SpendLogs]},
     },
+    include_in_schema=False,
 )
 async def get_global_activity_model(
     start_date: Optional[str] = fastapi.Query(
@@ -7463,6 +7467,7 @@ async def get_global_activity_model(
     responses={
         200: {"model": List[LiteLLM_SpendLogs]},
     },
+    include_in_schema=False,
 )
 async def get_global_activity_exceptions_per_deployment(
     model_group: str = fastapi.Query(
@@ -7615,6 +7620,7 @@ async def get_global_activity_exceptions_per_deployment(
     responses={
         200: {"model": List[LiteLLM_SpendLogs]},
     },
+    include_in_schema=False,
 )
 async def get_global_activity_exceptions(
     model_group: str = fastapi.Query(
@@ -8524,6 +8530,7 @@ async def global_spend_reset():
     "/global/spend/logs",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_spend_logs(
     api_key: str = fastapi.Query(
@@ -8569,6 +8576,7 @@ async def global_spend_logs(
     "/global/spend",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_spend():
     """
@@ -8595,6 +8603,7 @@ async def global_spend():
     "/global/spend/keys",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_spend_keys(
     limit: int = fastapi.Query(
@@ -8622,6 +8631,7 @@ async def global_spend_keys(
     "/global/spend/teams",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_spend_per_team():
     """
@@ -8746,6 +8756,7 @@ async def global_view_all_end_users():
     "/global/spend/end_users",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_spend_end_users(data: Optional[GlobalEndUsersSpend] = None):
     """
@@ -8798,6 +8809,7 @@ LIMIT 100
     "/global/spend/models",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_spend_models(
     limit: int = fastapi.Query(
@@ -8826,6 +8838,7 @@ async def global_spend_models(
     "/global/predict/spend/logs",
     tags=["Budget & Spend Tracking"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def global_predict_spend_logs(request: Request):
     from enterprise.utils import _forecast_daily_cost
@@ -12221,6 +12234,7 @@ async def alerting_settings(
     "/queue/chat/completions",
     tags=["experimental"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def async_queue_request(
     request: Request,
@@ -12332,18 +12346,10 @@ async def async_queue_request(
         )
 
 
-@router.get(
-    "/ollama_logs", dependencies=[Depends(user_api_key_auth)], tags=["experimental"]
-)
-async def retrieve_server_log(request: Request):
-    filepath = os.path.expanduser("~/.ollama/logs/server.log")
-    return FileResponse(filepath)
-
-
 #### LOGIN ENDPOINTS ####
 
 
-@app.get("/sso/key/generate", tags=["experimental"])
+@app.get("/sso/key/generate", tags=["experimental"], include_in_schema=False)
 async def google_login(request: Request):
     """
     Create Proxy API Keys using Google Workspace SSO. Requires setting PROXY_BASE_URL in .env
@@ -12937,7 +12943,7 @@ def get_image():
         return FileResponse(logo_path, media_type="image/jpeg")
 
 
-@app.get("/sso/callback", tags=["experimental"])
+@app.get("/sso/callback", tags=["experimental"], include_in_schema=False)
 async def auth_callback(request: Request):
     """Verify login"""
     global general_settings, ui_access_mode, premium_user
@@ -13242,6 +13248,7 @@ async def auth_callback(request: Request):
     tags=["Invite Links"],
     dependencies=[Depends(user_api_key_auth)],
     response_model=InvitationModel,
+    include_in_schema=False,
 )
 async def new_invitation(
     data: InvitationNew, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth)
@@ -13306,6 +13313,7 @@ async def new_invitation(
     tags=["Invite Links"],
     dependencies=[Depends(user_api_key_auth)],
     response_model=InvitationModel,
+    include_in_schema=False,
 )
 async def invitation_info(
     invitation_id: str, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth)
@@ -13357,6 +13365,7 @@ async def invitation_info(
     tags=["Invite Links"],
     dependencies=[Depends(user_api_key_auth)],
     response_model=InvitationModel,
+    include_in_schema=False,
 )
 async def invitation_update(
     data: InvitationUpdate,
@@ -13417,6 +13426,7 @@ async def invitation_update(
     tags=["Invite Links"],
     dependencies=[Depends(user_api_key_auth)],
     response_model=InvitationModel,
+    include_in_schema=False,
 )
 async def invitation_delete(
     data: InvitationDelete,
@@ -13469,6 +13479,7 @@ async def invitation_delete(
     "/config/update",
     tags=["config.yaml"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def update_config(config_info: ConfigYAML):
     """
@@ -13626,6 +13637,7 @@ Keep it more precise, to prevent overwrite other values unintentially
     "/config/field/update",
     tags=["config.yaml"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def update_config_general_settings(
     data: ConfigFieldUpdate,
@@ -13704,6 +13716,7 @@ async def update_config_general_settings(
     tags=["config.yaml"],
     dependencies=[Depends(user_api_key_auth)],
     response_model=ConfigFieldInfo,
+    include_in_schema=False,
 )
 async def get_config_general_settings(
     field_name: str,
@@ -13764,6 +13777,7 @@ async def get_config_general_settings(
     "/config/list",
     tags=["config.yaml"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def get_config_list(
     config_type: Literal["general_settings"],
@@ -13840,6 +13854,7 @@ async def get_config_list(
     "/config/field/delete",
     tags=["config.yaml"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def delete_config_general_settings(
     data: ConfigFieldDelete,
@@ -14095,6 +14110,7 @@ async def get_config():
     "/config/yaml",
     tags=["config.yaml"],
     dependencies=[Depends(user_api_key_auth)],
+    include_in_schema=False,
 )
 async def config_yaml_endpoint(config_info: ConfigYAML):
     """

From 95cc2484ed316e29dc0b43e1e5f6a31c2431c662 Mon Sep 17 00:00:00 2001
From: Ishaan Jaff 
Date: Thu, 13 Jun 2024 14:28:25 -0700
Subject: [PATCH 04/24] feat - add remaining team budget gauge

---
 litellm/integrations/prometheus.py      | 107 +++++++++++++++++++++---
 litellm/proxy/litellm_pre_call_utils.py |   5 ++
 litellm/proxy/proxy_config.yaml         |   3 +-
 3 files changed, 104 insertions(+), 11 deletions(-)

diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py
index c97fce0d83..7de8088c69 100644
--- a/litellm/integrations/prometheus.py
+++ b/litellm/integrations/prometheus.py
@@ -8,6 +8,7 @@ import traceback
 import datetime, subprocess, sys
 import litellm, uuid
 from litellm._logging import print_verbose, verbose_logger
+from typing import Optional, Union
 
 
 class PrometheusLogger:
@@ -17,33 +18,69 @@ class PrometheusLogger:
         **kwargs,
     ):
         try:
-            from prometheus_client import Counter
+            from prometheus_client import Counter, Gauge
 
             self.litellm_llm_api_failed_requests_metric = Counter(
                 name="litellm_llm_api_failed_requests_metric",
                 documentation="Total number of failed LLM API calls via litellm",
-                labelnames=["end_user", "hashed_api_key", "model", "team", "team_alias", "user"],
+                labelnames=[
+                    "end_user",
+                    "hashed_api_key",
+                    "model",
+                    "team",
+                    "team_alias",
+                    "user",
+                ],
             )
 
             self.litellm_requests_metric = Counter(
                 name="litellm_requests_metric",
                 documentation="Total number of LLM calls to litellm",
-                labelnames=["end_user", "hashed_api_key", "model", "team", "team_alias", "user"],
+                labelnames=[
+                    "end_user",
+                    "hashed_api_key",
+                    "model",
+                    "team",
+                    "team_alias",
+                    "user",
+                ],
             )
 
             # Counter for spend
             self.litellm_spend_metric = Counter(
                 "litellm_spend_metric",
                 "Total spend on LLM requests",
-                labelnames=["end_user", "hashed_api_key", "model", "team", "team_alias", "user"],
+                labelnames=[
+                    "end_user",
+                    "hashed_api_key",
+                    "model",
+                    "team",
+                    "team_alias",
+                    "user",
+                ],
             )
 
             # Counter for total_output_tokens
             self.litellm_tokens_metric = Counter(
                 "litellm_total_tokens",
                 "Total number of input + output tokens from LLM requests",
-                labelnames=["end_user", "hashed_api_key", "model", "team", "team_alias", "user"],
+                labelnames=[
+                    "end_user",
+                    "hashed_api_key",
+                    "model",
+                    "team",
+                    "team_alias",
+                    "user",
+                ],
             )
+
+            # Remaining Budget for Team, Key
+            self.litellm_remaining_team_budget_metric = Gauge(
+                "litellm_remaining_team_budget_metric",
+                "Remaining budget for team",
+                labelnames=["team_id", "team_alias"],
+            )
+
         except Exception as e:
             print_verbose(f"Got exception on init prometheus client {str(e)}")
             raise e
@@ -51,7 +88,9 @@ class PrometheusLogger:
     async def _async_log_event(
         self, kwargs, response_obj, start_time, end_time, print_verbose, user_id
     ):
-        self.log_event(kwargs, response_obj, start_time, end_time, print_verbose)
+        self.log_event(
+            kwargs, response_obj, start_time, end_time, user_id, print_verbose
+        )
 
     def log_event(
         self, kwargs, response_obj, start_time, end_time, user_id, print_verbose
@@ -78,6 +117,18 @@ class PrometheusLogger:
             user_api_team_alias = litellm_params.get("metadata", {}).get(
                 "user_api_key_team_alias", None
             )
+
+            _team_spend = litellm_params.get("metadata", {}).get(
+                "user_api_key_team_spend", None
+            )
+
+            _team_max_budget = litellm_params.get("metadata", {}).get(
+                "user_api_key_team_max_budget", None
+            )
+            _remaining_team_budget = safe_get_remaining_budget(
+                max_budget=_team_max_budget, spend=_team_spend
+            )
+
             if response_obj is not None:
                 tokens_used = response_obj.get("usage", {}).get("total_tokens", 0)
             else:
@@ -97,19 +148,43 @@ class PrometheusLogger:
                 user_api_key = hash_token(user_api_key)
 
             self.litellm_requests_metric.labels(
-                end_user_id, user_api_key, model, user_api_team, user_api_team_alias, user_id
+                end_user_id,
+                user_api_key,
+                model,
+                user_api_team,
+                user_api_team_alias,
+                user_id,
             ).inc()
             self.litellm_spend_metric.labels(
-                end_user_id, user_api_key, model, user_api_team, user_api_team_alias, user_id
+                end_user_id,
+                user_api_key,
+                model,
+                user_api_team,
+                user_api_team_alias,
+                user_id,
             ).inc(response_cost)
             self.litellm_tokens_metric.labels(
-                end_user_id, user_api_key, model, user_api_team, user_api_team_alias, user_id
+                end_user_id,
+                user_api_key,
+                model,
+                user_api_team,
+                user_api_team_alias,
+                user_id,
             ).inc(tokens_used)
 
+            self.litellm_remaining_team_budget_metric.labels(
+                user_api_team, user_api_team_alias
+            ).set(_remaining_team_budget)
+
             ### FAILURE INCREMENT ###
             if "exception" in kwargs:
                 self.litellm_llm_api_failed_requests_metric.labels(
-                    end_user_id, user_api_key, model, user_api_team, user_api_team_alias, user_id
+                    end_user_id,
+                    user_api_key,
+                    model,
+                    user_api_team,
+                    user_api_team_alias,
+                    user_id,
                 ).inc()
         except Exception as e:
             verbose_logger.error(
@@ -117,3 +192,15 @@ class PrometheusLogger:
             )
             verbose_logger.debug(traceback.format_exc())
             pass
+
+
+def safe_get_remaining_budget(
+    max_budget: Optional[float], spend: Optional[float]
+) -> float:
+    if max_budget is None:
+        return float("inf")
+
+    if spend is None:
+        return max_budget
+
+    return max_budget - spend
diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py
index d8dc798f08..fc6bd7044e 100644
--- a/litellm/proxy/litellm_pre_call_utils.py
+++ b/litellm/proxy/litellm_pre_call_utils.py
@@ -104,6 +104,11 @@ async def add_litellm_data_to_request(
     data["metadata"]["user_api_key_team_alias"] = getattr(
         user_api_key_dict, "team_alias", None
     )
+
+    # Team spend, budget - used by prometheus.py
+    data["metadata"]["user_api_key_team_max_budget"] = user_api_key_dict.team_max_budget
+    data["metadata"]["user_api_key_team_spend"] = user_api_key_dict.team_spend
+
     data["metadata"]["user_api_key_metadata"] = user_api_key_dict.metadata
     _headers = dict(request.headers)
     _headers.pop(
diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml
index d28f383c09..015e53ef15 100644
--- a/litellm/proxy/proxy_config.yaml
+++ b/litellm/proxy/proxy_config.yaml
@@ -22,7 +22,8 @@ general_settings:
   master_key: sk-1234
 
 litellm_settings:
-  callbacks: ["otel"]
+  success_callback: ["prometheus"]
+  failure_callback: ["prometheus"]
   store_audit_logs: true
   turn_off_message_logging: true
   redact_messages_in_exceptions: True

From fd3d764a3f7d47a09ed7aa6a4e0b96bdae80940b Mon Sep 17 00:00:00 2001
From: Ishaan Jaff 
Date: Thu, 13 Jun 2024 14:37:02 -0700
Subject: [PATCH 05/24] feat - add remaining budget for key on prometheus

---
 litellm/integrations/prometheus.py      | 27 +++++++++++++++++++++++--
 litellm/proxy/litellm_pre_call_utils.py |  4 ++++
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py
index 7de8088c69..4f0ffa387e 100644
--- a/litellm/integrations/prometheus.py
+++ b/litellm/integrations/prometheus.py
@@ -74,13 +74,20 @@ class PrometheusLogger:
                 ],
             )
 
-            # Remaining Budget for Team, Key
+            # Remaining Budget for Team
             self.litellm_remaining_team_budget_metric = Gauge(
                 "litellm_remaining_team_budget_metric",
                 "Remaining budget for team",
                 labelnames=["team_id", "team_alias"],
             )
 
+            # Remaining Budget for API Key
+            self.litellm_remaining_api_key_budget_metric = Gauge(
+                "litellm_remaining_api_key_budget_metric",
+                "Remaining budget for api key",
+                labelnames=["hashed_api_key", "api_key_alias"],
+            )
+
         except Exception as e:
             print_verbose(f"Got exception on init prometheus client {str(e)}")
             raise e
@@ -111,6 +118,9 @@ class PrometheusLogger:
                 "user_api_key_user_id", None
             )
             user_api_key = litellm_params.get("metadata", {}).get("user_api_key", None)
+            user_api_key_alias = litellm_params.get("metadata", {}).get(
+                "user_api_key_alias", None
+            )
             user_api_team = litellm_params.get("metadata", {}).get(
                 "user_api_key_team_id", None
             )
@@ -121,7 +131,6 @@ class PrometheusLogger:
             _team_spend = litellm_params.get("metadata", {}).get(
                 "user_api_key_team_spend", None
             )
-
             _team_max_budget = litellm_params.get("metadata", {}).get(
                 "user_api_key_team_max_budget", None
             )
@@ -129,6 +138,16 @@ class PrometheusLogger:
                 max_budget=_team_max_budget, spend=_team_spend
             )
 
+            _api_key_spend = litellm_params.get("metadata", {}).get(
+                "user_api_key_spend", None
+            )
+            _api_key_max_budget = litellm_params.get("metadata", {}).get(
+                "user_api_key_max_budget", None
+            )
+            _remaining_api_key_budget = safe_get_remaining_budget(
+                max_budget=_api_key_max_budget, spend=_api_key_spend
+            )
+
             if response_obj is not None:
                 tokens_used = response_obj.get("usage", {}).get("total_tokens", 0)
             else:
@@ -176,6 +195,10 @@ class PrometheusLogger:
                 user_api_team, user_api_team_alias
             ).set(_remaining_team_budget)
 
+            self.litellm_remaining_api_key_budget_metric.labels(
+                user_api_key, user_api_key_alias
+            ).set(_remaining_api_key_budget)
+
             ### FAILURE INCREMENT ###
             if "exception" in kwargs:
                 self.litellm_llm_api_failed_requests_metric.labels(
diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py
index fc6bd7044e..5aca61a96c 100644
--- a/litellm/proxy/litellm_pre_call_utils.py
+++ b/litellm/proxy/litellm_pre_call_utils.py
@@ -109,6 +109,10 @@ async def add_litellm_data_to_request(
     data["metadata"]["user_api_key_team_max_budget"] = user_api_key_dict.team_max_budget
     data["metadata"]["user_api_key_team_spend"] = user_api_key_dict.team_spend
 
+    # API Key spend, budget - used by prometheus.py
+    data["metadata"]["user_api_key_spend"] = user_api_key_dict.spend
+    data["metadata"]["user_api_key_max_budget"] = user_api_key_dict.max_budget
+
     data["metadata"]["user_api_key_metadata"] = user_api_key_dict.metadata
     _headers = dict(request.headers)
     _headers.pop(

From 36dd2a99d93995455e9424f4fefd413073884407 Mon Sep 17 00:00:00 2001
From: Ishaan Jaff 
Date: Thu, 13 Jun 2024 14:40:02 -0700
Subject: [PATCH 06/24] docs - budget metrics litellm

---
 docs/my-website/docs/proxy/prometheus.md | 9 ++++++++-
 docs/my-website/sidebars.js              | 2 +-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/docs/my-website/docs/proxy/prometheus.md b/docs/my-website/docs/proxy/prometheus.md
index b1fb62ad51..2c7481f4c6 100644
--- a/docs/my-website/docs/proxy/prometheus.md
+++ b/docs/my-website/docs/proxy/prometheus.md
@@ -1,4 +1,4 @@
-# Grafana, Prometheus metrics [BETA]
+# 📈 Prometheus metrics [BETA]
 
 LiteLLM Exposes a `/metrics` endpoint for Prometheus to Poll
 
@@ -54,6 +54,13 @@ http://localhost:4000/metrics
 | `litellm_total_tokens`         | input + output tokens per `"user", "key", "model", "team", "end-user"`     |
 | `litellm_llm_api_failed_requests_metric`   | Number of failed LLM API requests per `"user", "key", "model", "team", "end-user"`    |
 
+### Budget Metrics
+| Metric Name          | Description                          |
+|----------------------|--------------------------------------|
+| `litellm_remaining_team_budget_metric`             | Remaining Budget for Team (A team created on LiteLLM) |
+| `litellm_remaining_api_key_budget_metric`                | Remaining Budget for API Key (A key Created on LiteLLM)|
+
+
 ## Monitor System Health
 
 To monitor the health of litellm adjacent services (redis / postgres), do:
diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js
index 5618eb41ed..dc951ea05a 100644
--- a/docs/my-website/sidebars.js
+++ b/docs/my-website/sidebars.js
@@ -54,6 +54,7 @@ const sidebars = {
           items: ["proxy/logging", "proxy/streaming_logging"],
         },
         "proxy/ui",
+        "proxy/prometheus",
         "proxy/email",
         "proxy/multiple_admins",
         "proxy/team_based_routing",
@@ -70,7 +71,6 @@ const sidebars = {
         "proxy/pii_masking",
         "proxy/prompt_injection",
         "proxy/caching",
-        "proxy/prometheus",
         "proxy/call_hooks",
         "proxy/rules",
         "proxy/cli", 

From 8f77cfc013fa3df2304e3ada3b430f8f003057fd Mon Sep 17 00:00:00 2001
From: Ishaan Jaff 
Date: Thu, 13 Jun 2024 15:18:09 -0700
Subject: [PATCH 07/24] fix bug when updating team

---
 .../common_utils/management_endpoint_utils.py | 67 ++++++++++---------
 litellm/proxy/proxy_server.py                 |  1 +
 2 files changed, 35 insertions(+), 33 deletions(-)

diff --git a/litellm/proxy/common_utils/management_endpoint_utils.py b/litellm/proxy/common_utils/management_endpoint_utils.py
index 006f6aaae3..2aa4f9e70a 100644
--- a/litellm/proxy/common_utils/management_endpoint_utils.py
+++ b/litellm/proxy/common_utils/management_endpoint_utils.py
@@ -32,25 +32,25 @@ def management_endpoint_wrapper(func):
 
                 if open_telemetry_logger is not None:
                     _http_request: Request = kwargs.get("http_request")
+                    if _http_request:
+                        _route = _http_request.url.path
+                        _request_body: dict = await _read_request_body(
+                            request=_http_request
+                        )
+                        _response = dict(result) if result is not None else None
 
-                    _route = _http_request.url.path
-                    _request_body: dict = await _read_request_body(
-                        request=_http_request
-                    )
-                    _response = dict(result) if result is not None else None
+                        logging_payload = ManagementEndpointLoggingPayload(
+                            route=_route,
+                            request_data=_request_body,
+                            response=_response,
+                            start_time=start_time,
+                            end_time=end_time,
+                        )
 
-                    logging_payload = ManagementEndpointLoggingPayload(
-                        route=_route,
-                        request_data=_request_body,
-                        response=_response,
-                        start_time=start_time,
-                        end_time=end_time,
-                    )
-
-                    await open_telemetry_logger.async_management_endpoint_success_hook(
-                        logging_payload=logging_payload,
-                        parent_otel_span=parent_otel_span,
-                    )
+                        await open_telemetry_logger.async_management_endpoint_success_hook(
+                            logging_payload=logging_payload,
+                            parent_otel_span=parent_otel_span,
+                        )
 
             return result
         except Exception as e:
@@ -67,23 +67,24 @@ def management_endpoint_wrapper(func):
 
                 if open_telemetry_logger is not None:
                     _http_request: Request = kwargs.get("http_request")
-                    _route = _http_request.url.path
-                    _request_body: dict = await _read_request_body(
-                        request=_http_request
-                    )
-                    logging_payload = ManagementEndpointLoggingPayload(
-                        route=_route,
-                        request_data=_request_body,
-                        response=None,
-                        start_time=start_time,
-                        end_time=end_time,
-                        exception=e,
-                    )
+                    if _http_request:
+                        _route = _http_request.url.path
+                        _request_body: dict = await _read_request_body(
+                            request=_http_request
+                        )
+                        logging_payload = ManagementEndpointLoggingPayload(
+                            route=_route,
+                            request_data=_request_body,
+                            response=None,
+                            start_time=start_time,
+                            end_time=end_time,
+                            exception=e,
+                        )
 
-                    await open_telemetry_logger.async_management_endpoint_failure_hook(
-                        logging_payload=logging_payload,
-                        parent_otel_span=parent_otel_span,
-                    )
+                        await open_telemetry_logger.async_management_endpoint_failure_hook(
+                            logging_payload=logging_payload,
+                            parent_otel_span=parent_otel_span,
+                        )
 
             raise e
 
diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py
index dd8b2d61ab..ec9f716eb6 100644
--- a/litellm/proxy/proxy_server.py
+++ b/litellm/proxy/proxy_server.py
@@ -10181,6 +10181,7 @@ async def create_audit_log_for_update(request_data: LiteLLM_AuditLogs):
 @management_endpoint_wrapper
 async def update_team(
     data: UpdateTeamRequest,
+    http_request: Request,
     user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
     litellm_changed_by: Optional[str] = Header(
         None,

From b30856db7332df0335c023525cacaa09ba682da0 Mon Sep 17 00:00:00 2001
From: Ishaan Jaff 
Date: Thu, 13 Jun 2024 15:30:29 -0700
Subject: [PATCH 08/24] fix - ui show correct team budget when budget = 0.0

---
 ui/litellm-dashboard/src/components/teams.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ui/litellm-dashboard/src/components/teams.tsx b/ui/litellm-dashboard/src/components/teams.tsx
index 9e5758a283..56d4f5e0d6 100644
--- a/ui/litellm-dashboard/src/components/teams.tsx
+++ b/ui/litellm-dashboard/src/components/teams.tsx
@@ -427,7 +427,7 @@ const Team: React.FC = ({
                             overflow: "hidden",
                           }}
                         >
-                          {team["max_budget"] ? team["max_budget"] : "No limit"}
+                          {team["max_budget"] !== null && team["max_budget"] !== undefined ? team["max_budget"] : "No limit"}
                         
                         
Date: Thu, 13 Jun 2024 14:51:45 -0700
Subject: [PATCH 09/24] build(ui/teams.tsx): allow resetting teams budget

---
 .../src/components/create_key_button.tsx      |  1 +
 ui/litellm-dashboard/src/components/teams.tsx | 22 +++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/ui/litellm-dashboard/src/components/create_key_button.tsx b/ui/litellm-dashboard/src/components/create_key_button.tsx
index 83e8d92647..75edb2ef3b 100644
--- a/ui/litellm-dashboard/src/components/create_key_button.tsx
+++ b/ui/litellm-dashboard/src/components/create_key_button.tsx
@@ -261,6 +261,7 @@ const CreateKey: React.FC = ({
                 >
                   
                 
diff --git a/ui/litellm-dashboard/src/components/teams.tsx b/ui/litellm-dashboard/src/components/teams.tsx
index 56d4f5e0d6..a9b8ffb68b 100644
--- a/ui/litellm-dashboard/src/components/teams.tsx
+++ b/ui/litellm-dashboard/src/components/teams.tsx
@@ -151,6 +151,17 @@ const Team: React.FC = ({
             
               
             
+            
+                  
+                    daily
+                    weekly
+                    monthly
+                  
+                
             
               
             
@@ -635,6 +646,17 @@ const Team: React.FC = ({
                 
                   
                 
+                
+                  
+                    daily
+                    weekly
+                    monthly
+                  
+                
                 
Date: Thu, 13 Jun 2024 15:10:01 -0700
Subject: [PATCH 10/24] build: allow resetting customer budget weekly + edit
 customer budget panel

---
 .../src/components/budgets/budget_modal.tsx   |   1 +
 .../src/components/budgets/budget_panel.tsx   |  28 +++-
 .../components/budgets/edit_budget_modal.tsx  | 145 ++++++++++++++++++
 ui/litellm-dashboard/src/components/teams.tsx |  20 +--
 4 files changed, 183 insertions(+), 11 deletions(-)
 create mode 100644 ui/litellm-dashboard/src/components/budgets/edit_budget_modal.tsx

diff --git a/ui/litellm-dashboard/src/components/budgets/budget_modal.tsx b/ui/litellm-dashboard/src/components/budgets/budget_modal.tsx
index 2c32d88bd0..551e8d37a0 100644
--- a/ui/litellm-dashboard/src/components/budgets/budget_modal.tsx
+++ b/ui/litellm-dashboard/src/components/budgets/budget_modal.tsx
@@ -122,6 +122,7 @@ const BudgetModal: React.FC = ({
               >
                 
               
diff --git a/ui/litellm-dashboard/src/components/budgets/budget_panel.tsx b/ui/litellm-dashboard/src/components/budgets/budget_panel.tsx
index c52ca16318..4d2752a9b5 100644
--- a/ui/litellm-dashboard/src/components/budgets/budget_panel.tsx
+++ b/ui/litellm-dashboard/src/components/budgets/budget_panel.tsx
@@ -6,6 +6,7 @@
 import React, { useState, useEffect } from "react";
 import BudgetSettings from "./budget_settings";
 import BudgetModal from "./budget_modal";
+import EditBudgetModal from "./edit_budget_modal";
 import {
   Table,
   TableBody,
@@ -43,7 +44,7 @@ interface BudgetSettingsPageProps {
   accessToken: string | null;
 }
 
-interface budgetItem {
+export interface budgetItem {
   budget_id: string;
   max_budget: string | null;
   rpm_limit: number | null;
@@ -52,6 +53,8 @@ interface budgetItem {
 
 const BudgetPanel: React.FC = ({ accessToken }) => {
   const [isModalVisible, setIsModalVisible] = useState(false);
+  const [isEditModalVisible, setIsEditModalVisible] = useState(false);
+  const [selectedBudget, setSelectedBudget] = useState(null);
   const [budgetList, setBudgetList] = useState([]);
   useEffect(() => {
     if (!accessToken) {
@@ -62,6 +65,15 @@ const BudgetPanel: React.FC = ({ accessToken }) => {
     });
   }, [accessToken]);
 
+
+  const handleEditCall = async (budget_id: string, index: number) => {
+    if (accessToken == null) {
+      return;
+    }
+    setSelectedBudget(budgetList[index])
+    setIsEditModalVisible(true)
+  };
+  
   const handleDeleteCall = async (budget_id: string, index: number) => {
     if (accessToken == null) {
       return;
@@ -94,6 +106,15 @@ const BudgetPanel: React.FC = ({ accessToken }) => {
         setIsModalVisible={setIsModalVisible}
         setBudgetList={setBudgetList}
       />
+      {
+        selectedBudget && 
+      }
       
         Create a budget to assign to customers.
         
@@ -119,6 +140,11 @@ const BudgetPanel: React.FC = ({ accessToken }) => {
                 
                   {value.rpm_limit ? value.rpm_limit : "n/a"}
                 
+                 handleEditCall(value.budget_id, index)}
+                />
                 >;
+  setBudgetList: React.Dispatch>;
+  existingBudget: budgetItem
+}
+const EditBudgetModal: React.FC = ({
+  isModalVisible,
+  accessToken,
+  setIsModalVisible,
+  setBudgetList,
+  existingBudget
+}) => {
+  const [form] = Form.useForm();
+  const handleOk = () => {
+    setIsModalVisible(false);
+    form.resetFields();
+  };
+
+  const handleCancel = () => {
+    setIsModalVisible(false);
+    form.resetFields();
+  };
+
+  const handleCreate = async (formValues: Record) => {
+    if (accessToken == null || accessToken == undefined) {
+      return;
+    }
+    try {
+      message.info("Making API Call");
+      // setIsModalVisible(true);
+      const response = await budgetCreateCall(accessToken, formValues);
+      console.log("key create Response:", response);
+      setBudgetList((prevData) =>
+        prevData ? [...prevData, response] : [response]
+      ); // Check if prevData is null
+      message.success("API Key Created");
+      form.resetFields();
+    } catch (error) {
+      console.error("Error creating the key:", error);
+      message.error(`Error creating the key: ${error}`, 20);
+    }
+  };
+
+  return (
+    
+      
+ <> + + + + + + + + + + + + + Optional Settings + + + + + + + + + + + + +
+ Edit Budget +
+ +
+ ); +}; + +export default EditBudgetModal; diff --git a/ui/litellm-dashboard/src/components/teams.tsx b/ui/litellm-dashboard/src/components/teams.tsx index a9b8ffb68b..8b4b803b56 100644 --- a/ui/litellm-dashboard/src/components/teams.tsx +++ b/ui/litellm-dashboard/src/components/teams.tsx @@ -152,16 +152,16 @@ const Team: React.FC = ({ - - daily - weekly - monthly - - + className="mt-8" + label="Reset Budget" + name="budget_duration" + > + + daily + weekly + monthly + + From 92cd7ffc18742fde8f15e58998092134920691fa Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 13 Jun 2024 16:00:14 -0700 Subject: [PATCH 11/24] feat(__init__.py): allow setting drop_params as an env Closes https://github.com/BerriAI/litellm/issues/4175 --- litellm/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 91fa253e71..15f562d159 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -73,7 +73,7 @@ token: Optional[str] = ( ) telemetry = True max_tokens = 256 # OpenAI Defaults -drop_params = False +drop_params = bool(os.getenv("LITELLM_DROP_PARAMS", False)) modify_params = False retry = True ### AUTH ### From 34ccbfba5fe83f287298877c9816e5a990f78b47 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 15:56:43 -0700 Subject: [PATCH 12/24] doc - setting team budgets --- docs/my-website/docs/proxy/team_budgets.md | 98 ++++++++++++++++++++++ docs/my-website/sidebars.js | 1 + 2 files changed, 99 insertions(+) create mode 100644 docs/my-website/docs/proxy/team_budgets.md diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md new file mode 100644 index 0000000000..327bae34d0 --- /dev/null +++ b/docs/my-website/docs/proxy/team_budgets.md @@ -0,0 +1,98 @@ +import Image from '@theme/IdealImage'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Managing Team Budgets + +Track spend, set budgets for your Internal Team + +## Setting Monthly Team Budgets + +### 1. Create a team +with `max_budget` and `budget_duration` + + + + +Set `max_budget` and `budget_duration` +```shell +curl --location 'http://0.0.0.0:4000/team/new' \ + --header 'Authorization: Bearer sk-1234' \ + --header 'Content-Type: application/json' \ + --data '{ + "team_alias": "QA Prod Bot", + "max_budget": 0.000000001, + "budget_duration": "1d" + }' +``` + +Response +```shell +{ + "team_alias": "QA Prod Bot", + "team_id": "de35b29e-6ca8-4f47-b804-2b79d07aa99a", + "max_budget": 0.0001, + "budget_duration": "1d", + "budget_reset_at": "2024-06-14T22:48:36.594000Z" +} +``` + + + + + +### 2. Create a key for the `team` + +```shell +curl --location 'http://0.0.0.0:4000/key/generate' \ + --header 'Authorization: Bearer sk-1234' \ + --header 'Content-Type: application/json' \ + --data '{ + "team_id": "de35b29e-6ca8-4f47-b804-2b79d07aa99a" + }' +``` + +Response + +```shell +{"team_id":"de35b29e-6ca8-4f47-b804-2b79d07aa99a", "key":"sk-5qtncoYjzRcxMM4bDRktNQ"} +``` + + +### 3. Test It + +Run this Request twice +```shell +curl --location 'http://0.0.0.0:4000/chat/completions' \ +--header 'Authorization: Bearer sk-mso-JSykEGri86KyOvgxBw' \ +--header 'Content-Type: application/json' \ +--data ' { + "model": "llama3", + "messages": [ + { + "role": "user", + "content": "hi" + } + ], + } +' +``` + +On the 2nd response - expect to see the following exception + +```shell +{ + "error": { + "message": "Budget has been exceeded! Current cost: 3.5e-06, Max budget: 1e-09", + "type": "auth_error", + "param": null, + "code": 400 + } +} +``` + + +### 4. Prometheus metrics for `remaining_budget` + + +## Updating Team Budgets \ No newline at end of file diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index dc951ea05a..da9a99a953 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -44,6 +44,7 @@ const sidebars = { "proxy/self_serve", "proxy/users", "proxy/customers", + "proxy/team_budgets", "proxy/billing", "proxy/user_keys", "proxy/virtual_keys", From b4db497e23f4e664fd87c2e1abbf1bbd6fdef134 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 16:24:45 -0700 Subject: [PATCH 13/24] fix /team/update --- litellm/proxy/proxy_server.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index ec9f716eb6..6fecbce996 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -6644,7 +6644,7 @@ async def generate_key_fn( # Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True if litellm.store_audit_logs is True: - _updated_values = json.dumps(response) + _updated_values = json.dumps(response, default=str) asyncio.create_task( create_audit_log_for_update( request_data=LiteLLM_AuditLogs( @@ -6749,10 +6749,10 @@ async def update_key_fn( # Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True if litellm.store_audit_logs is True: - _updated_values = json.dumps(data_json) + _updated_values = json.dumps(data_json, default=str) _before_value = existing_key_row.json(exclude_none=True) - _before_value = json.dumps(_before_value) + _before_value = json.dumps(_before_value, default=str) asyncio.create_task( create_audit_log_for_update( @@ -6848,7 +6848,7 @@ async def delete_key_fn( ) key_row = key_row.json(exclude_none=True) - _key_row = json.dumps(key_row) + _key_row = json.dumps(key_row, default=str) asyncio.create_task( create_audit_log_for_update( @@ -9964,6 +9964,7 @@ async def new_team( - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - all keys associated with this team_id will have at max this RPM limit - max_budget: Optional[float] - The maximum budget allocated to the team - all keys for this team_id will have at max this max_budget + - budget_duration: Optional[str] - The budget duration for this team - Example "1s", "1d", "1m", "1y" - models: Optional[list] - A list of models associated with the team - all keys for this team_id will have at most, these models. If empty, assumes all models are allowed. - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id. @@ -10117,7 +10118,8 @@ async def new_team( # Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True if litellm.store_audit_logs is True: _updated_values = complete_team_data.json(exclude_none=True) - _updated_values = json.dumps(_updated_values) + + _updated_values = json.dumps(_updated_values, default=str) asyncio.create_task( create_audit_log_for_update( @@ -10256,8 +10258,8 @@ async def update_team( # Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True if litellm.store_audit_logs is True: _before_value = existing_team_row.json(exclude_none=True) - _before_value = json.dumps(_before_value) - _after_value: str = json.dumps(updated_kv) + _before_value = json.dumps(_before_value, default=str) + _after_value: str = json.dumps(updated_kv, default=str) asyncio.create_task( create_audit_log_for_update( From f903fb8de484f7fdc1a9cf406302bb734d8ae256 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 16:59:01 -0700 Subject: [PATCH 14/24] doc - team based budgets --- docs/my-website/docs/proxy/team_budgets.md | 94 ++++++++++++++-------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md index 327bae34d0..ff15eeacf3 100644 --- a/docs/my-website/docs/proxy/team_budgets.md +++ b/docs/my-website/docs/proxy/team_budgets.md @@ -2,28 +2,29 @@ import Image from '@theme/IdealImage'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# Managing Team Budgets +# Setting Team Budgets Track spend, set budgets for your Internal Team ## Setting Monthly Team Budgets ### 1. Create a team -with `max_budget` and `budget_duration` +- Set `max_budget=000000001` ($ value the team is allowed to spend) +- Set `budget_duration="1d"` (How frequently the budget should update) -Set `max_budget` and `budget_duration` +Create and new team and set `max_budget` and `budget_duration` ```shell -curl --location 'http://0.0.0.0:4000/team/new' \ - --header 'Authorization: Bearer sk-1234' \ - --header 'Content-Type: application/json' \ - --data '{ - "team_alias": "QA Prod Bot", - "max_budget": 0.000000001, - "budget_duration": "1d" - }' +curl -X POST 'http://0.0.0.0:4000/team/new' \ + -H 'Authorization: Bearer sk-1234' \ + -H 'Content-Type: application/json' \ + -d '{ + "team_alias": "QA Prod Bot", + "max_budget": 0.000000001, + "budget_duration": "1d" + }' ``` Response @@ -41,15 +42,29 @@ Response + +Possible values for `budget_duration` + +| `budget_duration` | When Budget will reset | +| --- | --- | +| `budget_duration="1s"` | every 1 second | +| `budget_duration="1m"` | every 1 min | +| `budget_duration="1h"` | every 1 hour | +| `budget_duration="1d"` | every 1 day | +| `budget_duration="30d"` | every 30 days | + + ### 2. Create a key for the `team` +Create a key for `team_id="de35b29e-6ca8-4f47-b804-2b79d07aa99a"` from Step 1 + +💡 **The Budget for Team="QA Prod Bot" budget will apply to this team** + ```shell -curl --location 'http://0.0.0.0:4000/key/generate' \ - --header 'Authorization: Bearer sk-1234' \ - --header 'Content-Type: application/json' \ - --data '{ - "team_id": "de35b29e-6ca8-4f47-b804-2b79d07aa99a" - }' +curl -X POST 'http://0.0.0.0:4000/key/generate' \ + -H 'Authorization: Bearer sk-1234' \ + -H 'Content-Type: application/json' \ + -d '{"team_id": "de35b29e-6ca8-4f47-b804-2b79d07aa99a"}' ``` Response @@ -61,21 +76,20 @@ Response ### 3. Test It -Run this Request twice +Use the key from step 2 and run this Request twice ```shell -curl --location 'http://0.0.0.0:4000/chat/completions' \ ---header 'Authorization: Bearer sk-mso-JSykEGri86KyOvgxBw' \ ---header 'Content-Type: application/json' \ ---data ' { - "model": "llama3", - "messages": [ - { - "role": "user", - "content": "hi" - } - ], - } -' +curl -X POST 'http://0.0.0.0:4000/chat/completions' \ + -H 'Authorization: Bearer sk-mso-JSykEGri86KyOvgxBw' \ + -H 'Content-Type: application/json' \ + -d ' { + "model": "llama3", + "messages": [ + { + "role": "user", + "content": "hi" + } + ] + }' ``` On the 2nd response - expect to see the following exception @@ -91,8 +105,22 @@ On the 2nd response - expect to see the following exception } ``` +## Advanced -### 4. Prometheus metrics for `remaining_budget` +### Prometheus metrics for `remaining_budget` + +You'll need the following in your proxy config.yaml + +```yaml +litellm_settings: + success_callback: ["prometheus"] + failure_callback: ["prometheus"] +``` + +Expect to see this metric on prometheus to track the Remaining Budget for the team + +```shell +litellm_remaining_team_budget_metric{team_alias="QA Prod Bot",team_id="de35b29e-6ca8-4f47-b804-2b79d07aa99a"} 9.699999999999992e-06 +``` -## Updating Team Budgets \ No newline at end of file From 6b3b7b72741752ac81950ec7120302eae4057f4a Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 17:01:27 -0700 Subject: [PATCH 15/24] doc - setting team budgets --- docs/my-website/docs/proxy/team_budgets.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md index ff15eeacf3..d5675ecc0e 100644 --- a/docs/my-website/docs/proxy/team_budgets.md +++ b/docs/my-website/docs/proxy/team_budgets.md @@ -2,7 +2,7 @@ import Image from '@theme/IdealImage'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# Setting Team Budgets +# 💰 Setting Team Budgets Track spend, set budgets for your Internal Team @@ -15,7 +15,7 @@ Track spend, set budgets for your Internal Team -Create and new team and set `max_budget` and `budget_duration` +Create a new team and set `max_budget` and `budget_duration` ```shell curl -X POST 'http://0.0.0.0:4000/team/new' \ -H 'Authorization: Bearer sk-1234' \ @@ -109,6 +109,8 @@ On the 2nd response - expect to see the following exception ### Prometheus metrics for `remaining_budget` +[More info about Prometheus metrics here](https://docs.litellm.ai/docs/proxy/prometheus) + You'll need the following in your proxy config.yaml ```yaml From b986aa2846b26fb7f052fca7be0b19b3ed42285b Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 17:31:15 -0700 Subject: [PATCH 16/24] update swagger for /team endpoints --- litellm/proxy/proxy_server.py | 37 ++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 6fecbce996..12e189998a 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -9954,17 +9954,18 @@ async def new_team( """ Allow users to create a new team. Apply user permissions to their team. - [ASK FOR HELP](https://github.com/BerriAI/litellm/issues) + 👉 [Detailed Doc on setting team budgets](https://docs.litellm.ai/docs/proxy/team_budgets) + Parameters: - team_alias: Optional[str] - User defined team alias - team_id: Optional[str] - The team id of the user. If none passed, we'll generate it. - members_with_roles: List[{"role": "admin" or "user", "user_id": ""}] - A list of users and their roles in the team. Get user_id when making a new user via `/user/new`. - - metadata: Optional[dict] - Metadata for team, store information for team. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } + - metadata: Optional[dict] - Metadata for team, store information for team. Example metadata = {"extra_info": "some info"} - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - all keys associated with this team_id will have at max this RPM limit - max_budget: Optional[float] - The maximum budget allocated to the team - all keys for this team_id will have at max this max_budget - - budget_duration: Optional[str] - The budget duration for this team - Example "1s", "1d", "1m", "1y" + - budget_duration: Optional[str] - The duration of the budget for the team. Doc [here](https://docs.litellm.ai/docs/proxy/team_budgets) - models: Optional[list] - A list of models associated with the team - all keys for this team_id will have at most, these models. If empty, assumes all models are allowed. - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id. @@ -9989,6 +9990,21 @@ async def new_team( {"role": "user", "user_id": "user-2434"}] }' + ``` + + ``` + curl --location 'http://0.0.0.0:4000/team/new' \ + + --header 'Authorization: Bearer sk-1234' \ + + --header 'Content-Type: application/json' \ + + --data '{ + "team_alias": "QA Prod Bot", + "max_budget": 0.000000001, + "budget_duration": "1d" + }' + ``` """ global prisma_client @@ -10202,6 +10218,7 @@ async def update_team( - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - all keys associated with this team_id will have at max this RPM limit - max_budget: Optional[float] - The maximum budget allocated to the team - all keys for this team_id will have at max this max_budget + - budget_duration: Optional[str] - The duration of the budget for the team. Doc [here](https://docs.litellm.ai/docs/proxy/team_budgets) - models: Optional[list] - A list of models associated with the team - all keys for this team_id will have at most, these models. If empty, assumes all models are allowed. - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id. @@ -10219,6 +10236,20 @@ async def update_team( "tpm_limit": 100 }' ``` + + Example - Update Team `max_budget` budget + ``` + curl --location 'http://0.0.0.0:8000/team/update' \ + + --header 'Authorization: Bearer sk-1234' \ + + --header 'Content-Type: application/json' \ + + --data-raw '{ + "team_id": "litellm-test-client-id-new", + "max_budget": 10 + }' + ``` """ global prisma_client From 9c7026e1a8e7e5a94f10b1d847dcaad6ff1d5b79 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 17:34:17 -0700 Subject: [PATCH 17/24] fix - update team --- litellm/tests/test_key_generate_prisma.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 21ffbdb547..e003622a88 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -2217,6 +2217,7 @@ async def test_create_update_team(prisma_client): tpm_limit=30, rpm_limit=30, ), + http_request=Request(scope={"type": "http"}), user_api_key_dict=UserAPIKeyAuth( user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234", From 988256a13ffac8f9c7d95ac73bc184776a954e1f Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 17:34:49 -0700 Subject: [PATCH 18/24] =?UTF-8?q?bump:=20version=201.40.10=20=E2=86=92=201?= =?UTF-8?q?.40.11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 462ebb6426..58e1e920ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.40.10" +version = "1.40.11" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -85,7 +85,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.40.10" +version = "1.40.11" version_files = [ "pyproject.toml:^version" ] From 75bfea4046a8fa91161e856da5fb35aff67ab283 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Jun 2024 19:02:29 -0700 Subject: [PATCH 19/24] doc fix creating team budgets --- docs/my-website/docs/proxy/team_budgets.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md index d5675ecc0e..30a1d818fb 100644 --- a/docs/my-website/docs/proxy/team_budgets.md +++ b/docs/my-website/docs/proxy/team_budgets.md @@ -12,8 +12,6 @@ Track spend, set budgets for your Internal Team - Set `max_budget=000000001` ($ value the team is allowed to spend) - Set `budget_duration="1d"` (How frequently the budget should update) - - Create a new team and set `max_budget` and `budget_duration` ```shell @@ -37,10 +35,7 @@ Response "budget_reset_at": "2024-06-14T22:48:36.594000Z" } ``` - - - - + Possible values for `budget_duration` From ed6a949f394052943df3954a21c75e061c51d6af Mon Sep 17 00:00:00 2001 From: lucca Date: Thu, 13 Jun 2024 21:24:07 -0300 Subject: [PATCH 20/24] llama 3 --- ...model_prices_and_context_window_backup.json | 18 ++++++++++++++++++ model_prices_and_context_window.json | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 385dc2ead0..98da8d69c0 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3347,6 +3347,24 @@ "litellm_provider": "deepinfra", "mode": "chat" }, + "deepinfra/meta-llama/Meta-Llama-3-8B-Instruct": { + "max_tokens": 8191, + "max_input_tokens": 8191, + "max_output_tokens": 4096, + "input_cost_per_token": 0.00000008, + "output_cost_per_token": 0.00000008, + "litellm_provider": "deepinfra", + "mode": "chat" + }, + "deepinfra/meta-llama/Meta-Llama-3-70B-Instruct": { + "max_tokens": 8191, + "max_input_tokens": 8191, + "max_output_tokens": 4096, + "input_cost_per_token": 0.00000059, + "output_cost_per_token": 0.00000079, + "litellm_provider": "deepinfra", + "mode": "chat" + }, "deepinfra/01-ai/Yi-34B-200K": { "max_tokens": 4096, "max_input_tokens": 200000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 385dc2ead0..98da8d69c0 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3347,6 +3347,24 @@ "litellm_provider": "deepinfra", "mode": "chat" }, + "deepinfra/meta-llama/Meta-Llama-3-8B-Instruct": { + "max_tokens": 8191, + "max_input_tokens": 8191, + "max_output_tokens": 4096, + "input_cost_per_token": 0.00000008, + "output_cost_per_token": 0.00000008, + "litellm_provider": "deepinfra", + "mode": "chat" + }, + "deepinfra/meta-llama/Meta-Llama-3-70B-Instruct": { + "max_tokens": 8191, + "max_input_tokens": 8191, + "max_output_tokens": 4096, + "input_cost_per_token": 0.00000059, + "output_cost_per_token": 0.00000079, + "litellm_provider": "deepinfra", + "mode": "chat" + }, "deepinfra/01-ai/Yi-34B-200K": { "max_tokens": 4096, "max_input_tokens": 200000, From b72d09689ebeeb6a5c2795c52482a276560bf043 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 13 Jun 2024 16:52:17 -0700 Subject: [PATCH 21/24] feat(proxy/utils.py): allow budget duration in months Closes https://github.com/BerriAI/litellm/issues/4042 --- litellm/proxy/utils.py | 40 ++++++++++++++++++++++++++++++++++--- litellm/tests/test_utils.py | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 54782c0887..ba5df81b7c 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1,4 +1,4 @@ -from typing import Optional, List, Any, Literal, Union, TYPE_CHECKING +from typing import Optional, List, Any, Literal, Union, TYPE_CHECKING, Tuple import os import subprocess import hashlib @@ -2093,14 +2093,32 @@ def get_logging_payload( raise e -def _duration_in_seconds(duration: str): - match = re.match(r"(\d+)([smhd]?)", duration) +def _extract_from_regex(duration: str) -> Tuple[int, str]: + match = re.match(r"(\d+)(mo|[smhd]?)", duration) + if not match: raise ValueError("Invalid duration format") value, unit = match.groups() value = int(value) + return value, unit + + +def _duration_in_seconds(duration: str) -> int: + """ + Parameters: + - duration: + - "s" - seconds + - "m" - minutes + - "h" - hours + - "d" - days + - "mo" - months + + Returns time in seconds till when budget needs to be reset + """ + value, unit = _extract_from_regex(duration=duration) + if unit == "s": return value elif unit == "m": @@ -2109,6 +2127,22 @@ def _duration_in_seconds(duration: str): return value * 3600 elif unit == "d": return value * 86400 + elif unit == "mo": + now = time.time() + current_time = datetime.fromtimestamp(now) + + # Calculate the first day of the next month + if current_time.month == 12: + next_month = datetime(year=current_time.year + 1, month=1, day=1) + else: + next_month = datetime( + year=current_time.year, month=current_time.month + value, day=1 + ) + + # Calculate the duration until the first day of the next month + duration_until_next_month = next_month - current_time + return int(duration_until_next_month.total_seconds()) + else: raise ValueError("Unsupported duration unit") diff --git a/litellm/tests/test_utils.py b/litellm/tests/test_utils.py index 2e32e32df7..742199c7f9 100644 --- a/litellm/tests/test_utils.py +++ b/litellm/tests/test_utils.py @@ -26,6 +26,7 @@ from litellm.utils import ( get_max_tokens, get_supported_openai_params, ) +from litellm.proxy.utils import _duration_in_seconds, _extract_from_regex # Assuming your trim_messages, shorten_message_to_fit_limit, and get_token_count functions are all in a module named 'message_utils' @@ -445,3 +446,40 @@ def test_redact_msgs_from_logs(): litellm.turn_off_message_logging = False print("Test passed") + + +@pytest.mark.parametrize( + "duration, unit", + [("7s", "s"), ("7m", "m"), ("7h", "h"), ("7d", "d"), ("7mo", "mo")], +) +def test_extract_from_regex(duration, unit): + value, _unit = _extract_from_regex(duration=duration) + + assert value == 7 + assert _unit == unit + + +def test_duration_in_seconds(): + """ + Test if duration int is correctly calculated for different str + """ + import time + + now = time.time() + current_time = datetime.fromtimestamp(now) + print("current_time={}".format(current_time)) + # Calculate the first day of the next month + if current_time.month == 12: + next_month = datetime(year=current_time.year + 1, month=1, day=1) + else: + next_month = datetime( + year=current_time.year, month=current_time.month + 1, day=1 + ) + print("next_month={}".format(next_month)) + # Calculate the duration until the first day of the next month + duration_until_next_month = next_month - current_time + expected_duration = int(duration_until_next_month.total_seconds()) + + value = _duration_in_seconds(duration="1mo") + + assert value - expected_duration < 2 From 11555e56ed74a1415a35194c5bc7db81d1bde9a7 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 13 Jun 2024 20:45:48 -0700 Subject: [PATCH 22/24] build(ui): new build --- litellm/proxy/_experimental/out/404.html | 1 + .../out/_next/static/chunks/131-6a03368053f9d26d.js | 8 -------- .../out/_next/static/chunks/131-cb6bfe24e23e121b.js | 8 ++++++++ .../out/_next/static/chunks/294-0e35509d5ca95267.js | 13 ------------- .../static/chunks/2f6dbc85-052c4579f80d66ae.js | 1 - .../static/chunks/2f6dbc85-cac2949a76539886.js | 1 + .../static/chunks/3014691f-589a5f4865c3822f.js | 1 - .../static/chunks/3014691f-b24e8254c7593934.js | 1 + .../out/_next/static/chunks/505-5ff3c318fddfa35c.js | 13 +++++++++++++ .../out/_next/static/chunks/684-16b194c83a169f6d.js | 1 + .../out/_next/static/chunks/684-bb2d2f93d92acb0b.js | 1 - .../out/_next/static/chunks/69-04708d7d4a17c1ee.js | 1 - .../out/_next/static/chunks/69-8316d07d1f41e39f.js | 1 + .../out/_next/static/chunks/759-83a8bdddfe32b5d9.js | 13 ------------- .../out/_next/static/chunks/759-c0083d8a782d300e.js | 13 +++++++++++++ ...81b72386c2.js => _not-found-4163791cb6a88df1.js} | 2 +- ...bef188642a56c0.js => layout-1c3f654c7747e999.js} | 2 +- .../chunks/app/model_hub/page-4cb65c32467214b5.js | 1 - .../chunks/app/model_hub/page-a1942d43573c82c3.js | 1 + .../chunks/app/onboarding/page-49a30e653b6ae929.js | 1 + .../chunks/app/onboarding/page-664c7288e11fff5a.js | 1 - .../static/chunks/app/page-8028473f1a04553d.js | 1 + .../static/chunks/app/page-d301c202a2cebcd3.js | 1 - .../static/chunks/fd9d1056-f593049e31b05aeb.js | 1 + .../static/chunks/fd9d1056-f960ab1e6d32b002.js | 1 - .../_next/static/chunks/main-160227023782230a.js | 1 - .../_next/static/chunks/main-a61244f130fbf565.js | 2 +- ...b13a7db53edf.js => main-app-096338c8e1915716.js} | 2 +- ...e007a0280178b.js => webpack-887c75b16b85d4b4.js} | 2 +- .../out/_next/static/css/159e0004b5599df8.css | 5 ----- .../out/_next/static/css/63f65dbb14efd996.css | 5 +++++ .../_buildManifest.js | 0 .../_ssgManifest.js | 0 litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- litellm/proxy/_experimental/out/model_hub.html | 1 + litellm/proxy/_experimental/out/model_hub.txt | 4 ++-- litellm/proxy/_experimental/out/onboarding.html | 1 + litellm/proxy/_experimental/out/onboarding.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../static/Q9smtS3bJUKJtn7pvgodO/_buildManifest.js | 1 - .../static/Q9smtS3bJUKJtn7pvgodO/_ssgManifest.js | 1 - .../out/_next/static/chunks/131-6a03368053f9d26d.js | 8 -------- .../out/_next/static/chunks/294-0e35509d5ca95267.js | 13 ------------- .../static/chunks/2f6dbc85-052c4579f80d66ae.js | 1 - .../static/chunks/3014691f-589a5f4865c3822f.js | 1 - .../out/_next/static/chunks/684-bb2d2f93d92acb0b.js | 1 - .../out/_next/static/chunks/69-04708d7d4a17c1ee.js | 1 - .../out/_next/static/chunks/759-83a8bdddfe32b5d9.js | 13 ------------- .../chunks/app/_not-found-b1ee1381b72386c2.js | 1 - .../static/chunks/app/layout-9bbef188642a56c0.js | 1 - .../chunks/app/model_hub/page-4cb65c32467214b5.js | 1 - .../chunks/app/onboarding/page-664c7288e11fff5a.js | 1 - .../static/chunks/app/page-d301c202a2cebcd3.js | 1 - .../static/chunks/fd9d1056-f960ab1e6d32b002.js | 1 - .../static/chunks/main-app-9b4fb13a7db53edf.js | 1 - .../_next/static/chunks/webpack-496e007a0280178b.js | 1 - .../out/_next/static/css/159e0004b5599df8.css | 5 ----- ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- ui/litellm-dashboard/out/model_hub.html | 2 +- ui/litellm-dashboard/out/model_hub.txt | 4 ++-- ui/litellm-dashboard/out/onboarding.html | 2 +- ui/litellm-dashboard/out/onboarding.txt | 4 ++-- 64 files changed, 72 insertions(+), 123 deletions(-) create mode 100644 litellm/proxy/_experimental/out/404.html delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/131-6a03368053f9d26d.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/131-cb6bfe24e23e121b.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/294-0e35509d5ca95267.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/2f6dbc85-052c4579f80d66ae.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/2f6dbc85-cac2949a76539886.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/3014691f-589a5f4865c3822f.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/3014691f-b24e8254c7593934.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/505-5ff3c318fddfa35c.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/684-16b194c83a169f6d.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/684-bb2d2f93d92acb0b.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/69-04708d7d4a17c1ee.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/69-8316d07d1f41e39f.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/759-83a8bdddfe32b5d9.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/759-c0083d8a782d300e.js rename litellm/proxy/_experimental/out/_next/static/chunks/app/{_not-found-b1ee1381b72386c2.js => _not-found-4163791cb6a88df1.js} (93%) rename litellm/proxy/_experimental/out/_next/static/chunks/app/{layout-9bbef188642a56c0.js => layout-1c3f654c7747e999.js} (60%) delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/model_hub/page-4cb65c32467214b5.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/model_hub/page-a1942d43573c82c3.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/onboarding/page-49a30e653b6ae929.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/onboarding/page-664c7288e11fff5a.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-8028473f1a04553d.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-d301c202a2cebcd3.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/fd9d1056-f593049e31b05aeb.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/fd9d1056-f960ab1e6d32b002.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/main-160227023782230a.js rename ui/litellm-dashboard/out/_next/static/chunks/main-160227023782230a.js => litellm/proxy/_experimental/out/_next/static/chunks/main-a61244f130fbf565.js (99%) rename litellm/proxy/_experimental/out/_next/static/chunks/{main-app-9b4fb13a7db53edf.js => main-app-096338c8e1915716.js} (54%) rename litellm/proxy/_experimental/out/_next/static/chunks/{webpack-496e007a0280178b.js => webpack-887c75b16b85d4b4.js} (96%) delete mode 100644 litellm/proxy/_experimental/out/_next/static/css/159e0004b5599df8.css create mode 100644 litellm/proxy/_experimental/out/_next/static/css/63f65dbb14efd996.css rename litellm/proxy/_experimental/out/_next/static/{Q9smtS3bJUKJtn7pvgodO => sTvd1VbHSi_TBr1KiIpul}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{Q9smtS3bJUKJtn7pvgodO => sTvd1VbHSi_TBr1KiIpul}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/model_hub.html create mode 100644 litellm/proxy/_experimental/out/onboarding.html delete mode 100644 ui/litellm-dashboard/out/_next/static/Q9smtS3bJUKJtn7pvgodO/_buildManifest.js delete mode 100644 ui/litellm-dashboard/out/_next/static/Q9smtS3bJUKJtn7pvgodO/_ssgManifest.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/131-6a03368053f9d26d.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/294-0e35509d5ca95267.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/2f6dbc85-052c4579f80d66ae.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/3014691f-589a5f4865c3822f.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/684-bb2d2f93d92acb0b.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/69-04708d7d4a17c1ee.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/759-83a8bdddfe32b5d9.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/_not-found-b1ee1381b72386c2.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/layout-9bbef188642a56c0.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/model_hub/page-4cb65c32467214b5.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/onboarding/page-664c7288e11fff5a.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-d301c202a2cebcd3.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/fd9d1056-f960ab1e6d32b002.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/webpack-496e007a0280178b.js delete mode 100644 ui/litellm-dashboard/out/_next/static/css/159e0004b5599df8.css diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html new file mode 100644 index 0000000000..f2e61a560d --- /dev/null +++ b/litellm/proxy/_experimental/out/404.html @@ -0,0 +1 @@ +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/static/chunks/131-6a03368053f9d26d.js b/litellm/proxy/_experimental/out/_next/static/chunks/131-6a03368053f9d26d.js deleted file mode 100644 index f6ea1fb198..0000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/131-6a03368053f9d26d.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[131],{84174:function(e,t,n){n.d(t,{Z:function(){return s}});var a=n(14749),r=n(64090),i={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"},o=n(60688),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},50459:function(e,t,n){n.d(t,{Z:function(){return s}});var a=n(14749),r=n(64090),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"}}]},name:"right",theme:"outlined"},o=n(60688),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},92836:function(e,t,n){n.d(t,{Z:function(){return p}});var a=n(69703),r=n(80991),i=n(2898),o=n(99250),s=n(65492),l=n(64090),c=n(41608),d=n(50027);n(18174),n(21871),n(41213);let u=(0,s.fn)("Tab"),p=l.forwardRef((e,t)=>{let{icon:n,className:p,children:g}=e,m=(0,a._T)(e,["icon","className","children"]),b=(0,l.useContext)(c.O),f=(0,l.useContext)(d.Z);return l.createElement(r.O,Object.assign({ref:t,className:(0,o.q)(u("root"),"flex whitespace-nowrap truncate max-w-xs outline-none focus:ring-0 text-tremor-default transition duration-100",f?(0,s.bM)(f,i.K.text).selectTextColor:"solid"===b?"ui-selected:text-tremor-content-emphasis dark:ui-selected:text-dark-tremor-content-emphasis":"ui-selected:text-tremor-brand dark:ui-selected:text-dark-tremor-brand",function(e,t){switch(e){case"line":return(0,o.q)("ui-selected:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","dark:hover:border-dark-tremor-content-emphasis dark:hover:text-dark-tremor-content-emphasis dark:text-dark-tremor-content",t?(0,s.bM)(t,i.K.border).selectBorderColor:"ui-selected:border-tremor-brand dark:ui-selected:border-dark-tremor-brand");case"solid":return(0,o.q)("border-transparent border rounded-tremor-small px-2.5 py-1","ui-selected:border-tremor-border ui-selected:bg-tremor-background ui-selected:shadow-tremor-input hover:text-tremor-content-emphasis ui-selected:text-tremor-brand","dark:ui-selected:border-dark-tremor-border dark:ui-selected:bg-dark-tremor-background dark:ui-selected:shadow-dark-tremor-input dark:hover:text-dark-tremor-content-emphasis dark:ui-selected:text-dark-tremor-brand",t?(0,s.bM)(t,i.K.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,f),p)},m),n?l.createElement(n,{className:(0,o.q)(u("icon"),"flex-none h-5 w-5",g?"mr-2":"")}):null,g?l.createElement("span",null,g):null)});p.displayName="Tab"},26734:function(e,t,n){n.d(t,{Z:function(){return c}});var a=n(69703),r=n(80991),i=n(99250),o=n(65492),s=n(64090);let l=(0,o.fn)("TabGroup"),c=s.forwardRef((e,t)=>{let{defaultIndex:n,index:o,onIndexChange:c,children:d,className:u}=e,p=(0,a._T)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.createElement(r.O.Group,Object.assign({as:"div",ref:t,defaultIndex:n,selectedIndex:o,onChange:c,className:(0,i.q)(l("root"),"w-full",u)},p),d)});c.displayName="TabGroup"},41608:function(e,t,n){n.d(t,{O:function(){return c},Z:function(){return u}});var a=n(69703),r=n(64090),i=n(50027);n(18174),n(21871),n(41213);var o=n(80991),s=n(99250);let l=(0,n(65492).fn)("TabList"),c=(0,r.createContext)("line"),d={line:(0,s.q)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,s.q)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},u=r.forwardRef((e,t)=>{let{color:n,variant:u="line",children:p,className:g}=e,m=(0,a._T)(e,["color","variant","children","className"]);return r.createElement(o.O.List,Object.assign({ref:t,className:(0,s.q)(l("root"),"justify-start overflow-x-clip",d[u],g)},m),r.createElement(c.Provider,{value:u},r.createElement(i.Z.Provider,{value:n},p)))});u.displayName="TabList"},32126:function(e,t,n){n.d(t,{Z:function(){return d}});var a=n(69703);n(50027);var r=n(18174);n(21871);var i=n(41213),o=n(99250),s=n(65492),l=n(64090);let c=(0,s.fn)("TabPanel"),d=l.forwardRef((e,t)=>{let{children:n,className:s}=e,d=(0,a._T)(e,["children","className"]),{selectedValue:u}=(0,l.useContext)(i.Z),p=u===(0,l.useContext)(r.Z);return l.createElement("div",Object.assign({ref:t,className:(0,o.q)(c("root"),"w-full mt-2",p?"":"hidden",s),"aria-selected":p?"true":"false"},d),n)});d.displayName="TabPanel"},23682:function(e,t,n){n.d(t,{Z:function(){return u}});var a=n(69703),r=n(80991);n(50027);var i=n(18174);n(21871);var o=n(41213),s=n(99250),l=n(65492),c=n(64090);let d=(0,l.fn)("TabPanels"),u=c.forwardRef((e,t)=>{let{children:n,className:l}=e,u=(0,a._T)(e,["children","className"]);return c.createElement(r.O.Panels,Object.assign({as:"div",ref:t,className:(0,s.q)(d("root"),"w-full",l)},u),e=>{let{selectedIndex:t}=e;return c.createElement(o.Z.Provider,{value:{selectedValue:t}},c.Children.map(n,(e,t)=>c.createElement(i.Z.Provider,{value:t},e)))})});u.displayName="TabPanels"},50027:function(e,t,n){n.d(t,{Z:function(){return i}});var a=n(64090),r=n(54942);n(99250);let i=(0,a.createContext)(r.fr.Blue)},18174:function(e,t,n){n.d(t,{Z:function(){return a}});let a=(0,n(64090).createContext)(0)},21871:function(e,t,n){n.d(t,{Z:function(){return a}});let a=(0,n(64090).createContext)(void 0)},41213:function(e,t,n){n.d(t,{Z:function(){return a}});let a=(0,n(64090).createContext)({selectedValue:void 0,handleValueChange:void 0})},21467:function(e,t,n){n.d(t,{i:function(){return s}});var a=n(64090),r=n(44329),i=n(54165),o=n(57499);function s(e){return t=>a.createElement(i.ZP,{theme:{token:{motion:!1,zIndexPopupBase:0}}},a.createElement(e,Object.assign({},t)))}t.Z=(e,t,n,i)=>s(s=>{let{prefixCls:l,style:c}=s,d=a.useRef(null),[u,p]=a.useState(0),[g,m]=a.useState(0),[b,f]=(0,r.Z)(!1,{value:s.open}),{getPrefixCls:E}=a.useContext(o.E_),h=E(t||"select",l);a.useEffect(()=>{if(f(!0),"undefined"!=typeof ResizeObserver){let e=new ResizeObserver(e=>{let t=e[0].target;p(t.offsetHeight+8),m(t.offsetWidth)}),t=setInterval(()=>{var a;let r=n?".".concat(n(h)):".".concat(h,"-dropdown"),i=null===(a=d.current)||void 0===a?void 0:a.querySelector(r);i&&(clearInterval(t),e.observe(i))},10);return()=>{clearInterval(t),e.disconnect()}}},[]);let S=Object.assign(Object.assign({},s),{style:Object.assign(Object.assign({},c),{margin:0}),open:b,visible:b,getPopupContainer:()=>d.current});return i&&(S=i(S)),a.createElement("div",{ref:d,style:{paddingBottom:u,position:"relative",minWidth:g}},a.createElement(e,Object.assign({},S)))})},99129:function(e,t,n){let a;n.d(t,{Z:function(){return eY}});var r=n(63787),i=n(64090),o=n(37274),s=n(57499),l=n(54165),c=n(99537),d=n(77136),u=n(20653),p=n(40388),g=n(16480),m=n.n(g),b=n(51761),f=n(47387),E=n(70595),h=n(24750),S=n(89211),y=n(1861),T=n(51350),A=e=>{let{type:t,children:n,prefixCls:a,buttonProps:r,close:o,autoFocus:s,emitEvent:l,isSilent:c,quitOnNullishReturnValue:d,actionFn:u}=e,p=i.useRef(!1),g=i.useRef(null),[m,b]=(0,S.Z)(!1),f=function(){null==o||o.apply(void 0,arguments)};i.useEffect(()=>{let e=null;return s&&(e=setTimeout(()=>{var e;null===(e=g.current)||void 0===e||e.focus()})),()=>{e&&clearTimeout(e)}},[]);let E=e=>{e&&e.then&&(b(!0),e.then(function(){b(!1,!0),f.apply(void 0,arguments),p.current=!1},e=>{if(b(!1,!0),p.current=!1,null==c||!c())return Promise.reject(e)}))};return i.createElement(y.ZP,Object.assign({},(0,T.nx)(t),{onClick:e=>{let t;if(!p.current){if(p.current=!0,!u){f();return}if(l){var n;if(t=u(e),d&&!((n=t)&&n.then)){p.current=!1,f(e);return}}else if(u.length)t=u(o),p.current=!1;else if(!(t=u())){f();return}E(t)}},loading:m,prefixCls:a},r,{ref:g}),n)};let R=i.createContext({}),{Provider:I}=R;var N=()=>{let{autoFocusButton:e,cancelButtonProps:t,cancelTextLocale:n,isSilent:a,mergedOkCancel:r,rootPrefixCls:o,close:s,onCancel:l,onConfirm:c}=(0,i.useContext)(R);return r?i.createElement(A,{isSilent:a,actionFn:l,close:function(){null==s||s.apply(void 0,arguments),null==c||c(!1)},autoFocus:"cancel"===e,buttonProps:t,prefixCls:"".concat(o,"-btn")},n):null},_=()=>{let{autoFocusButton:e,close:t,isSilent:n,okButtonProps:a,rootPrefixCls:r,okTextLocale:o,okType:s,onConfirm:l,onOk:c}=(0,i.useContext)(R);return i.createElement(A,{isSilent:n,type:s||"primary",actionFn:c,close:function(){null==t||t.apply(void 0,arguments),null==l||l(!0)},autoFocus:"ok"===e,buttonProps:a,prefixCls:"".concat(r,"-btn")},o)},v=n(81303),w=n(14749),k=n(80406),C=n(88804),O=i.createContext({}),x=n(5239),L=n(31506),D=n(91010),P=n(4295),M=n(72480);function F(e,t,n){var a=t;return!a&&n&&(a="".concat(e,"-").concat(n)),a}function U(e,t){var n=e["page".concat(t?"Y":"X","Offset")],a="scroll".concat(t?"Top":"Left");if("number"!=typeof n){var r=e.document;"number"!=typeof(n=r.documentElement[a])&&(n=r.body[a])}return n}var B=n(49367),G=n(74084),$=i.memo(function(e){return e.children},function(e,t){return!t.shouldUpdate}),H={width:0,height:0,overflow:"hidden",outline:"none"},z=i.forwardRef(function(e,t){var n,a,r,o=e.prefixCls,s=e.className,l=e.style,c=e.title,d=e.ariaId,u=e.footer,p=e.closable,g=e.closeIcon,b=e.onClose,f=e.children,E=e.bodyStyle,h=e.bodyProps,S=e.modalRender,y=e.onMouseDown,T=e.onMouseUp,A=e.holderRef,R=e.visible,I=e.forceRender,N=e.width,_=e.height,v=e.classNames,k=e.styles,C=i.useContext(O).panel,L=(0,G.x1)(A,C),D=(0,i.useRef)(),P=(0,i.useRef)();i.useImperativeHandle(t,function(){return{focus:function(){var e;null===(e=D.current)||void 0===e||e.focus()},changeActive:function(e){var t=document.activeElement;e&&t===P.current?D.current.focus():e||t!==D.current||P.current.focus()}}});var M={};void 0!==N&&(M.width=N),void 0!==_&&(M.height=_),u&&(n=i.createElement("div",{className:m()("".concat(o,"-footer"),null==v?void 0:v.footer),style:(0,x.Z)({},null==k?void 0:k.footer)},u)),c&&(a=i.createElement("div",{className:m()("".concat(o,"-header"),null==v?void 0:v.header),style:(0,x.Z)({},null==k?void 0:k.header)},i.createElement("div",{className:"".concat(o,"-title"),id:d},c))),p&&(r=i.createElement("button",{type:"button",onClick:b,"aria-label":"Close",className:"".concat(o,"-close")},g||i.createElement("span",{className:"".concat(o,"-close-x")})));var F=i.createElement("div",{className:m()("".concat(o,"-content"),null==v?void 0:v.content),style:null==k?void 0:k.content},r,a,i.createElement("div",(0,w.Z)({className:m()("".concat(o,"-body"),null==v?void 0:v.body),style:(0,x.Z)((0,x.Z)({},E),null==k?void 0:k.body)},h),f),n);return i.createElement("div",{key:"dialog-element",role:"dialog","aria-labelledby":c?d:null,"aria-modal":"true",ref:L,style:(0,x.Z)((0,x.Z)({},l),M),className:m()(o,s),onMouseDown:y,onMouseUp:T},i.createElement("div",{tabIndex:0,ref:D,style:H,"aria-hidden":"true"}),i.createElement($,{shouldUpdate:R||I},S?S(F):F),i.createElement("div",{tabIndex:0,ref:P,style:H,"aria-hidden":"true"}))}),j=i.forwardRef(function(e,t){var n=e.prefixCls,a=e.title,r=e.style,o=e.className,s=e.visible,l=e.forceRender,c=e.destroyOnClose,d=e.motionName,u=e.ariaId,p=e.onVisibleChanged,g=e.mousePosition,b=(0,i.useRef)(),f=i.useState(),E=(0,k.Z)(f,2),h=E[0],S=E[1],y={};function T(){var e,t,n,a,r,i=(n={left:(t=(e=b.current).getBoundingClientRect()).left,top:t.top},r=(a=e.ownerDocument).defaultView||a.parentWindow,n.left+=U(r),n.top+=U(r,!0),n);S(g?"".concat(g.x-i.left,"px ").concat(g.y-i.top,"px"):"")}return h&&(y.transformOrigin=h),i.createElement(B.ZP,{visible:s,onVisibleChanged:p,onAppearPrepare:T,onEnterPrepare:T,forceRender:l,motionName:d,removeOnLeave:c,ref:b},function(s,l){var c=s.className,d=s.style;return i.createElement(z,(0,w.Z)({},e,{ref:t,title:a,ariaId:u,prefixCls:n,holderRef:l,style:(0,x.Z)((0,x.Z)((0,x.Z)({},d),r),y),className:m()(o,c)}))})});function V(e){var t=e.prefixCls,n=e.style,a=e.visible,r=e.maskProps,o=e.motionName,s=e.className;return i.createElement(B.ZP,{key:"mask",visible:a,motionName:o,leavedClassName:"".concat(t,"-mask-hidden")},function(e,a){var o=e.className,l=e.style;return i.createElement("div",(0,w.Z)({ref:a,style:(0,x.Z)((0,x.Z)({},l),n),className:m()("".concat(t,"-mask"),o,s)},r))})}function W(e){var t=e.prefixCls,n=void 0===t?"rc-dialog":t,a=e.zIndex,r=e.visible,o=void 0!==r&&r,s=e.keyboard,l=void 0===s||s,c=e.focusTriggerAfterClose,d=void 0===c||c,u=e.wrapStyle,p=e.wrapClassName,g=e.wrapProps,b=e.onClose,f=e.afterOpenChange,E=e.afterClose,h=e.transitionName,S=e.animation,y=e.closable,T=e.mask,A=void 0===T||T,R=e.maskTransitionName,I=e.maskAnimation,N=e.maskClosable,_=e.maskStyle,v=e.maskProps,C=e.rootClassName,O=e.classNames,U=e.styles,B=(0,i.useRef)(),G=(0,i.useRef)(),$=(0,i.useRef)(),H=i.useState(o),z=(0,k.Z)(H,2),W=z[0],q=z[1],Y=(0,D.Z)();function K(e){null==b||b(e)}var Z=(0,i.useRef)(!1),X=(0,i.useRef)(),Q=null;return(void 0===N||N)&&(Q=function(e){Z.current?Z.current=!1:G.current===e.target&&K(e)}),(0,i.useEffect)(function(){o&&(q(!0),(0,L.Z)(G.current,document.activeElement)||(B.current=document.activeElement))},[o]),(0,i.useEffect)(function(){return function(){clearTimeout(X.current)}},[]),i.createElement("div",(0,w.Z)({className:m()("".concat(n,"-root"),C)},(0,M.Z)(e,{data:!0})),i.createElement(V,{prefixCls:n,visible:A&&o,motionName:F(n,R,I),style:(0,x.Z)((0,x.Z)({zIndex:a},_),null==U?void 0:U.mask),maskProps:v,className:null==O?void 0:O.mask}),i.createElement("div",(0,w.Z)({tabIndex:-1,onKeyDown:function(e){if(l&&e.keyCode===P.Z.ESC){e.stopPropagation(),K(e);return}o&&e.keyCode===P.Z.TAB&&$.current.changeActive(!e.shiftKey)},className:m()("".concat(n,"-wrap"),p,null==O?void 0:O.wrapper),ref:G,onClick:Q,style:(0,x.Z)((0,x.Z)((0,x.Z)({zIndex:a},u),null==U?void 0:U.wrapper),{},{display:W?null:"none"})},g),i.createElement(j,(0,w.Z)({},e,{onMouseDown:function(){clearTimeout(X.current),Z.current=!0},onMouseUp:function(){X.current=setTimeout(function(){Z.current=!1})},ref:$,closable:void 0===y||y,ariaId:Y,prefixCls:n,visible:o&&W,onClose:K,onVisibleChanged:function(e){if(e)!function(){if(!(0,L.Z)(G.current,document.activeElement)){var e;null===(e=$.current)||void 0===e||e.focus()}}();else{if(q(!1),A&&B.current&&d){try{B.current.focus({preventScroll:!0})}catch(e){}B.current=null}W&&(null==E||E())}null==f||f(e)},motionName:F(n,h,S)}))))}j.displayName="Content",n(53850);var q=function(e){var t=e.visible,n=e.getContainer,a=e.forceRender,r=e.destroyOnClose,o=void 0!==r&&r,s=e.afterClose,l=e.panelRef,c=i.useState(t),d=(0,k.Z)(c,2),u=d[0],p=d[1],g=i.useMemo(function(){return{panel:l}},[l]);return(i.useEffect(function(){t&&p(!0)},[t]),a||!o||u)?i.createElement(O.Provider,{value:g},i.createElement(C.Z,{open:t||a||u,autoDestroy:!1,getContainer:n,autoLock:t||u},i.createElement(W,(0,w.Z)({},e,{destroyOnClose:o,afterClose:function(){null==s||s(),p(!1)}})))):null};q.displayName="Dialog";var Y=function(e,t,n){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:i.createElement(v.Z,null),r=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if("boolean"==typeof e?!e:void 0===t?!r:!1===t||null===t)return[!1,null];let o="boolean"==typeof t||null==t?a:t;return[!0,n?n(o):o]},K=n(22127),Z=n(86718),X=n(47137),Q=n(92801),J=n(48563);function ee(){}let et=i.createContext({add:ee,remove:ee});var en=n(17094),ea=()=>{let{cancelButtonProps:e,cancelTextLocale:t,onCancel:n}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({onClick:n},e),t)},er=()=>{let{confirmLoading:e,okButtonProps:t,okType:n,okTextLocale:a,onOk:r}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({},(0,T.nx)(n),{loading:e,onClick:r},t),a)},ei=n(4678);function eo(e,t){return i.createElement("span",{className:"".concat(e,"-close-x")},t||i.createElement(v.Z,{className:"".concat(e,"-close-icon")}))}let es=e=>{let t;let{okText:n,okType:a="primary",cancelText:o,confirmLoading:s,onOk:l,onCancel:c,okButtonProps:d,cancelButtonProps:u,footer:p}=e,[g]=(0,E.Z)("Modal",(0,ei.A)()),m={confirmLoading:s,okButtonProps:d,cancelButtonProps:u,okTextLocale:n||(null==g?void 0:g.okText),cancelTextLocale:o||(null==g?void 0:g.cancelText),okType:a,onOk:l,onCancel:c},b=i.useMemo(()=>m,(0,r.Z)(Object.values(m)));return"function"==typeof p||void 0===p?(t=i.createElement(i.Fragment,null,i.createElement(ea,null),i.createElement(er,null)),"function"==typeof p&&(t=p(t,{OkBtn:er,CancelBtn:ea})),t=i.createElement(I,{value:b},t)):t=p,i.createElement(en.n,{disabled:!1},t)};var el=n(11303),ec=n(13703),ed=n(58854),eu=n(80316),ep=n(76585),eg=n(8985);function em(e){return{position:e,inset:0}}let eb=e=>{let{componentCls:t,antCls:n}=e;return[{["".concat(t,"-root")]:{["".concat(t).concat(n,"-zoom-enter, ").concat(t).concat(n,"-zoom-appear")]:{transform:"none",opacity:0,animationDuration:e.motionDurationSlow,userSelect:"none"},["".concat(t).concat(n,"-zoom-leave ").concat(t,"-content")]:{pointerEvents:"none"},["".concat(t,"-mask")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,height:"100%",backgroundColor:e.colorBgMask,pointerEvents:"none",["".concat(t,"-hidden")]:{display:"none"}}),["".concat(t,"-wrap")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,overflow:"auto",outline:0,WebkitOverflowScrolling:"touch",["&:has(".concat(t).concat(n,"-zoom-enter), &:has(").concat(t).concat(n,"-zoom-appear)")]:{pointerEvents:"none"}})}},{["".concat(t,"-root")]:(0,ec.J$)(e)}]},ef=e=>{let{componentCls:t}=e;return[{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl"},["".concat(t,"-centered")]:{textAlign:"center","&::before":{display:"inline-block",width:0,height:"100%",verticalAlign:"middle",content:'""'},[t]:{top:0,display:"inline-block",paddingBottom:0,textAlign:"start",verticalAlign:"middle"}},["@media (max-width: ".concat(e.screenSMMax,"px)")]:{[t]:{maxWidth:"calc(100vw - 16px)",margin:"".concat((0,eg.bf)(e.marginXS)," auto")},["".concat(t,"-centered")]:{[t]:{flex:1}}}}},{[t]:Object.assign(Object.assign({},(0,el.Wf)(e)),{pointerEvents:"none",position:"relative",top:100,width:"auto",maxWidth:"calc(100vw - ".concat((0,eg.bf)(e.calc(e.margin).mul(2).equal()),")"),margin:"0 auto",paddingBottom:e.paddingLG,["".concat(t,"-title")]:{margin:0,color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.titleFontSize,lineHeight:e.titleLineHeight,wordWrap:"break-word"},["".concat(t,"-content")]:{position:"relative",backgroundColor:e.contentBg,backgroundClip:"padding-box",border:0,borderRadius:e.borderRadiusLG,boxShadow:e.boxShadow,pointerEvents:"auto",padding:e.contentPadding},["".concat(t,"-close")]:Object.assign({position:"absolute",top:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),insetInlineEnd:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),zIndex:e.calc(e.zIndexPopupBase).add(10).equal(),padding:0,color:e.modalCloseIconColor,fontWeight:e.fontWeightStrong,lineHeight:1,textDecoration:"none",background:"transparent",borderRadius:e.borderRadiusSM,width:e.modalCloseBtnSize,height:e.modalCloseBtnSize,border:0,outline:0,cursor:"pointer",transition:"color ".concat(e.motionDurationMid,", background-color ").concat(e.motionDurationMid),"&-x":{display:"flex",fontSize:e.fontSizeLG,fontStyle:"normal",lineHeight:"".concat((0,eg.bf)(e.modalCloseBtnSize)),justifyContent:"center",textTransform:"none",textRendering:"auto"},"&:hover":{color:e.modalIconHoverColor,backgroundColor:e.closeBtnHoverBg,textDecoration:"none"},"&:active":{backgroundColor:e.closeBtnActiveBg}},(0,el.Qy)(e)),["".concat(t,"-header")]:{color:e.colorText,background:e.headerBg,borderRadius:"".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)," 0 0"),marginBottom:e.headerMarginBottom,padding:e.headerPadding,borderBottom:e.headerBorderBottom},["".concat(t,"-body")]:{fontSize:e.fontSize,lineHeight:e.lineHeight,wordWrap:"break-word",padding:e.bodyPadding},["".concat(t,"-footer")]:{textAlign:"end",background:e.footerBg,marginTop:e.footerMarginTop,padding:e.footerPadding,borderTop:e.footerBorderTop,borderRadius:e.footerBorderRadius,["> ".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginInlineStart:e.marginXS}},["".concat(t,"-open")]:{overflow:"hidden"}})},{["".concat(t,"-pure-panel")]:{top:"auto",padding:0,display:"flex",flexDirection:"column",["".concat(t,"-content,\n ").concat(t,"-body,\n ").concat(t,"-confirm-body-wrapper")]:{display:"flex",flexDirection:"column",flex:"auto"},["".concat(t,"-confirm-body")]:{marginBottom:"auto"}}}]},eE=e=>{let{componentCls:t}=e;return{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl",["".concat(t,"-confirm-body")]:{direction:"rtl"}}}}},eh=e=>{let t=e.padding,n=e.fontSizeHeading5,a=e.lineHeightHeading5;return(0,eu.TS)(e,{modalHeaderHeight:e.calc(e.calc(a).mul(n).equal()).add(e.calc(t).mul(2).equal()).equal(),modalFooterBorderColorSplit:e.colorSplit,modalFooterBorderStyle:e.lineType,modalFooterBorderWidth:e.lineWidth,modalIconHoverColor:e.colorIconHover,modalCloseIconColor:e.colorIcon,modalCloseBtnSize:e.fontHeight,modalConfirmIconSize:e.fontHeight,modalTitleHeight:e.calc(e.titleFontSize).mul(e.titleLineHeight).equal()})},eS=e=>({footerBg:"transparent",headerBg:e.colorBgElevated,titleLineHeight:e.lineHeightHeading5,titleFontSize:e.fontSizeHeading5,contentBg:e.colorBgElevated,titleColor:e.colorTextHeading,closeBtnHoverBg:e.wireframe?"transparent":e.colorFillContent,closeBtnActiveBg:e.wireframe?"transparent":e.colorFillContentHover,contentPadding:e.wireframe?0:"".concat((0,eg.bf)(e.paddingMD)," ").concat((0,eg.bf)(e.paddingContentHorizontalLG)),headerPadding:e.wireframe?"".concat((0,eg.bf)(e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,headerBorderBottom:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",headerMarginBottom:e.wireframe?0:e.marginXS,bodyPadding:e.wireframe?e.paddingLG:0,footerPadding:e.wireframe?"".concat((0,eg.bf)(e.paddingXS)," ").concat((0,eg.bf)(e.padding)):0,footerBorderTop:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",footerBorderRadius:e.wireframe?"0 0 ".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)):0,footerMarginTop:e.wireframe?0:e.marginSM,confirmBodyPadding:e.wireframe?"".concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,confirmIconMarginInlineEnd:e.wireframe?e.margin:e.marginSM,confirmBtnsMarginTop:e.wireframe?e.marginLG:e.marginSM});var ey=(0,ep.I$)("Modal",e=>{let t=eh(e);return[ef(t),eE(t),eb(t),(0,ed._y)(t,"zoom")]},eS,{unitless:{titleLineHeight:!0}}),eT=n(92935),eA=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};(0,K.Z)()&&window.document.documentElement&&document.documentElement.addEventListener("click",e=>{a={x:e.pageX,y:e.pageY},setTimeout(()=>{a=null},100)},!0);var eR=e=>{var t;let{getPopupContainer:n,getPrefixCls:r,direction:o,modal:l}=i.useContext(s.E_),c=t=>{let{onCancel:n}=e;null==n||n(t)},{prefixCls:d,className:u,rootClassName:p,open:g,wrapClassName:E,centered:h,getContainer:S,closeIcon:y,closable:T,focusTriggerAfterClose:A=!0,style:R,visible:I,width:N=520,footer:_,classNames:w,styles:k}=e,C=eA(e,["prefixCls","className","rootClassName","open","wrapClassName","centered","getContainer","closeIcon","closable","focusTriggerAfterClose","style","visible","width","footer","classNames","styles"]),O=r("modal",d),x=r(),L=(0,eT.Z)(O),[D,P,M]=ey(O,L),F=m()(E,{["".concat(O,"-centered")]:!!h,["".concat(O,"-wrap-rtl")]:"rtl"===o}),U=null!==_&&i.createElement(es,Object.assign({},e,{onOk:t=>{let{onOk:n}=e;null==n||n(t)},onCancel:c})),[B,G]=Y(T,y,e=>eo(O,e),i.createElement(v.Z,{className:"".concat(O,"-close-icon")}),!0),$=function(e){let t=i.useContext(et),n=i.useRef();return(0,J.zX)(a=>{if(a){let r=e?a.querySelector(e):a;t.add(r),n.current=r}else t.remove(n.current)})}(".".concat(O,"-content")),[H,z]=(0,b.Cn)("Modal",C.zIndex);return D(i.createElement(Q.BR,null,i.createElement(X.Ux,{status:!0,override:!0},i.createElement(Z.Z.Provider,{value:z},i.createElement(q,Object.assign({width:N},C,{zIndex:H,getContainer:void 0===S?n:S,prefixCls:O,rootClassName:m()(P,p,M,L),footer:U,visible:null!=g?g:I,mousePosition:null!==(t=C.mousePosition)&&void 0!==t?t:a,onClose:c,closable:B,closeIcon:G,focusTriggerAfterClose:A,transitionName:(0,f.m)(x,"zoom",e.transitionName),maskTransitionName:(0,f.m)(x,"fade",e.maskTransitionName),className:m()(P,u,null==l?void 0:l.className),style:Object.assign(Object.assign({},null==l?void 0:l.style),R),classNames:Object.assign(Object.assign({wrapper:F},null==l?void 0:l.classNames),w),styles:Object.assign(Object.assign({},null==l?void 0:l.styles),k),panelRef:$}))))))};let eI=e=>{let{componentCls:t,titleFontSize:n,titleLineHeight:a,modalConfirmIconSize:r,fontSize:i,lineHeight:o,modalTitleHeight:s,fontHeight:l,confirmBodyPadding:c}=e,d="".concat(t,"-confirm");return{[d]:{"&-rtl":{direction:"rtl"},["".concat(e.antCls,"-modal-header")]:{display:"none"},["".concat(d,"-body-wrapper")]:Object.assign({},(0,el.dF)()),["&".concat(t," ").concat(t,"-body")]:{padding:c},["".concat(d,"-body")]:{display:"flex",flexWrap:"nowrap",alignItems:"start",["> ".concat(e.iconCls)]:{flex:"none",fontSize:r,marginInlineEnd:e.confirmIconMarginInlineEnd,marginTop:e.calc(e.calc(l).sub(r).equal()).div(2).equal()},["&-has-title > ".concat(e.iconCls)]:{marginTop:e.calc(e.calc(s).sub(r).equal()).div(2).equal()}},["".concat(d,"-paragraph")]:{display:"flex",flexDirection:"column",flex:"auto",rowGap:e.marginXS,maxWidth:"calc(100% - ".concat((0,eg.bf)(e.calc(e.modalConfirmIconSize).add(e.marginSM).equal()),")")},["".concat(d,"-title")]:{color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:n,lineHeight:a},["".concat(d,"-content")]:{color:e.colorText,fontSize:i,lineHeight:o},["".concat(d,"-btns")]:{textAlign:"end",marginTop:e.confirmBtnsMarginTop,["".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginBottom:0,marginInlineStart:e.marginXS}}},["".concat(d,"-error ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorError},["".concat(d,"-warning ").concat(d,"-body > ").concat(e.iconCls,",\n ").concat(d,"-confirm ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorWarning},["".concat(d,"-info ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorInfo},["".concat(d,"-success ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorSuccess}}};var eN=(0,ep.bk)(["Modal","confirm"],e=>[eI(eh(e))],eS,{order:-1e3}),e_=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};function ev(e){let{prefixCls:t,icon:n,okText:a,cancelText:o,confirmPrefixCls:s,type:l,okCancel:g,footer:b,locale:f}=e,h=e_(e,["prefixCls","icon","okText","cancelText","confirmPrefixCls","type","okCancel","footer","locale"]),S=n;if(!n&&null!==n)switch(l){case"info":S=i.createElement(p.Z,null);break;case"success":S=i.createElement(c.Z,null);break;case"error":S=i.createElement(d.Z,null);break;default:S=i.createElement(u.Z,null)}let y=null!=g?g:"confirm"===l,T=null!==e.autoFocusButton&&(e.autoFocusButton||"ok"),[A]=(0,E.Z)("Modal"),R=f||A,v=a||(y?null==R?void 0:R.okText:null==R?void 0:R.justOkText),w=Object.assign({autoFocusButton:T,cancelTextLocale:o||(null==R?void 0:R.cancelText),okTextLocale:v,mergedOkCancel:y},h),k=i.useMemo(()=>w,(0,r.Z)(Object.values(w))),C=i.createElement(i.Fragment,null,i.createElement(N,null),i.createElement(_,null)),O=void 0!==e.title&&null!==e.title,x="".concat(s,"-body");return i.createElement("div",{className:"".concat(s,"-body-wrapper")},i.createElement("div",{className:m()(x,{["".concat(x,"-has-title")]:O})},S,i.createElement("div",{className:"".concat(s,"-paragraph")},O&&i.createElement("span",{className:"".concat(s,"-title")},e.title),i.createElement("div",{className:"".concat(s,"-content")},e.content))),void 0===b||"function"==typeof b?i.createElement(I,{value:k},i.createElement("div",{className:"".concat(s,"-btns")},"function"==typeof b?b(C,{OkBtn:_,CancelBtn:N}):C)):b,i.createElement(eN,{prefixCls:t}))}let ew=e=>{let{close:t,zIndex:n,afterClose:a,open:r,keyboard:o,centered:s,getContainer:l,maskStyle:c,direction:d,prefixCls:u,wrapClassName:p,rootPrefixCls:g,bodyStyle:E,closable:S=!1,closeIcon:y,modalRender:T,focusTriggerAfterClose:A,onConfirm:R,styles:I}=e,N="".concat(u,"-confirm"),_=e.width||416,v=e.style||{},w=void 0===e.mask||e.mask,k=void 0!==e.maskClosable&&e.maskClosable,C=m()(N,"".concat(N,"-").concat(e.type),{["".concat(N,"-rtl")]:"rtl"===d},e.className),[,O]=(0,h.ZP)(),x=i.useMemo(()=>void 0!==n?n:O.zIndexPopupBase+b.u6,[n,O]);return i.createElement(eR,{prefixCls:u,className:C,wrapClassName:m()({["".concat(N,"-centered")]:!!e.centered},p),onCancel:()=>{null==t||t({triggerCancel:!0}),null==R||R(!1)},open:r,title:"",footer:null,transitionName:(0,f.m)(g||"","zoom",e.transitionName),maskTransitionName:(0,f.m)(g||"","fade",e.maskTransitionName),mask:w,maskClosable:k,style:v,styles:Object.assign({body:E,mask:c},I),width:_,zIndex:x,afterClose:a,keyboard:o,centered:s,getContainer:l,closable:S,closeIcon:y,modalRender:T,focusTriggerAfterClose:A},i.createElement(ev,Object.assign({},e,{confirmPrefixCls:N})))};var ek=e=>{let{rootPrefixCls:t,iconPrefixCls:n,direction:a,theme:r}=e;return i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:n,direction:a,theme:r},i.createElement(ew,Object.assign({},e)))},eC=[];let eO="",ex=e=>{var t,n;let{prefixCls:a,getContainer:r,direction:o}=e,l=(0,ei.A)(),c=(0,i.useContext)(s.E_),d=eO||c.getPrefixCls(),u=a||"".concat(d,"-modal"),p=r;return!1===p&&(p=void 0),i.createElement(ek,Object.assign({},e,{rootPrefixCls:d,prefixCls:u,iconPrefixCls:c.iconPrefixCls,theme:c.theme,direction:null!=o?o:c.direction,locale:null!==(n=null===(t=c.locale)||void 0===t?void 0:t.Modal)&&void 0!==n?n:l,getContainer:p}))};function eL(e){let t;let n=(0,l.w6)(),a=document.createDocumentFragment(),s=Object.assign(Object.assign({},e),{close:u,open:!0});function c(){for(var t=arguments.length,n=Array(t),i=0;ie&&e.triggerCancel);e.onCancel&&s&&e.onCancel.apply(e,[()=>{}].concat((0,r.Z)(n.slice(1))));for(let e=0;e{let t=n.getPrefixCls(void 0,eO),r=n.getIconPrefixCls(),s=n.getTheme(),c=i.createElement(ex,Object.assign({},e));(0,o.s)(i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:r,theme:s},n.holderRender?n.holderRender(c):c),a)})}function u(){for(var t=arguments.length,n=Array(t),a=0;a{"function"==typeof e.afterClose&&e.afterClose(),c.apply(this,n)}})).visible&&delete s.visible,d(s)}return d(s),eC.push(u),{destroy:u,update:function(e){d(s="function"==typeof e?e(s):Object.assign(Object.assign({},s),e))}}}function eD(e){return Object.assign(Object.assign({},e),{type:"warning"})}function eP(e){return Object.assign(Object.assign({},e),{type:"info"})}function eM(e){return Object.assign(Object.assign({},e),{type:"success"})}function eF(e){return Object.assign(Object.assign({},e),{type:"error"})}function eU(e){return Object.assign(Object.assign({},e),{type:"confirm"})}var eB=n(21467),eG=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},e$=(0,eB.i)(e=>{let{prefixCls:t,className:n,closeIcon:a,closable:r,type:o,title:l,children:c,footer:d}=e,u=eG(e,["prefixCls","className","closeIcon","closable","type","title","children","footer"]),{getPrefixCls:p}=i.useContext(s.E_),g=p(),b=t||p("modal"),f=(0,eT.Z)(g),[E,h,S]=ey(b,f),y="".concat(b,"-confirm"),T={};return T=o?{closable:null!=r&&r,title:"",footer:"",children:i.createElement(ev,Object.assign({},e,{prefixCls:b,confirmPrefixCls:y,rootPrefixCls:g,content:c}))}:{closable:null==r||r,title:l,footer:null!==d&&i.createElement(es,Object.assign({},e)),children:c},E(i.createElement(z,Object.assign({prefixCls:b,className:m()(h,"".concat(b,"-pure-panel"),o&&y,o&&"".concat(y,"-").concat(o),n,S,f)},u,{closeIcon:eo(b,a),closable:r},T)))}),eH=n(79474),ez=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},ej=i.forwardRef((e,t)=>{var n,{afterClose:a,config:o}=e,l=ez(e,["afterClose","config"]);let[c,d]=i.useState(!0),[u,p]=i.useState(o),{direction:g,getPrefixCls:m}=i.useContext(s.E_),b=m("modal"),f=m(),h=function(){d(!1);for(var e=arguments.length,t=Array(e),n=0;ne&&e.triggerCancel);u.onCancel&&a&&u.onCancel.apply(u,[()=>{}].concat((0,r.Z)(t.slice(1))))};i.useImperativeHandle(t,()=>({destroy:h,update:e=>{p(t=>Object.assign(Object.assign({},t),e))}}));let S=null!==(n=u.okCancel)&&void 0!==n?n:"confirm"===u.type,[y]=(0,E.Z)("Modal",eH.Z.Modal);return i.createElement(ek,Object.assign({prefixCls:b,rootPrefixCls:f},u,{close:h,open:c,afterClose:()=>{var e;a(),null===(e=u.afterClose)||void 0===e||e.call(u)},okText:u.okText||(S?null==y?void 0:y.okText:null==y?void 0:y.justOkText),direction:u.direction||g,cancelText:u.cancelText||(null==y?void 0:y.cancelText)},l))});let eV=0,eW=i.memo(i.forwardRef((e,t)=>{let[n,a]=function(){let[e,t]=i.useState([]);return[e,i.useCallback(e=>(t(t=>[].concat((0,r.Z)(t),[e])),()=>{t(t=>t.filter(t=>t!==e))}),[])]}();return i.useImperativeHandle(t,()=>({patchElement:a}),[]),i.createElement(i.Fragment,null,n)}));function eq(e){return eL(eD(e))}eR.useModal=function(){let e=i.useRef(null),[t,n]=i.useState([]);i.useEffect(()=>{t.length&&((0,r.Z)(t).forEach(e=>{e()}),n([]))},[t]);let a=i.useCallback(t=>function(a){var o;let s,l;eV+=1;let c=i.createRef(),d=new Promise(e=>{s=e}),u=!1,p=i.createElement(ej,{key:"modal-".concat(eV),config:t(a),ref:c,afterClose:()=>{null==l||l()},isSilent:()=>u,onConfirm:e=>{s(e)}});return(l=null===(o=e.current)||void 0===o?void 0:o.patchElement(p))&&eC.push(l),{destroy:()=>{function e(){var e;null===(e=c.current)||void 0===e||e.destroy()}c.current?e():n(t=>[].concat((0,r.Z)(t),[e]))},update:e=>{function t(){var t;null===(t=c.current)||void 0===t||t.update(e)}c.current?t():n(e=>[].concat((0,r.Z)(e),[t]))},then:e=>(u=!0,d.then(e))}},[]);return[i.useMemo(()=>({info:a(eP),success:a(eM),error:a(eF),warning:a(eD),confirm:a(eU)}),[]),i.createElement(eW,{key:"modal-holder",ref:e})]},eR.info=function(e){return eL(eP(e))},eR.success=function(e){return eL(eM(e))},eR.error=function(e){return eL(eF(e))},eR.warning=eq,eR.warn=eq,eR.confirm=function(e){return eL(eU(e))},eR.destroyAll=function(){for(;eC.length;){let e=eC.pop();e&&e()}},eR.config=function(e){let{rootPrefixCls:t}=e;eO=t},eR._InternalPanelDoNotUseOrYouWillBeFired=e$;var eY=eR},13703:function(e,t,n){n.d(t,{J$:function(){return s}});var a=n(8985),r=n(59353);let i=new a.E4("antFadeIn",{"0%":{opacity:0},"100%":{opacity:1}}),o=new a.E4("antFadeOut",{"0%":{opacity:1},"100%":{opacity:0}}),s=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],{antCls:n}=e,a="".concat(n,"-fade"),s=t?"&":"";return[(0,r.R)(a,i,o,e.motionDurationMid,t),{["\n ".concat(s).concat(a,"-enter,\n ").concat(s).concat(a,"-appear\n ")]:{opacity:0,animationTimingFunction:"linear"},["".concat(s).concat(a,"-leave")]:{animationTimingFunction:"linear"}}]}},44056:function(e){e.exports=function(e,n){for(var a,r,i,o=e||"",s=n||"div",l={},c=0;c4&&m.slice(0,4)===o&&s.test(t)&&("-"===t.charAt(4)?b=o+(n=t.slice(5).replace(l,u)).charAt(0).toUpperCase()+n.slice(1):(g=(p=t).slice(4),t=l.test(g)?p:("-"!==(g=g.replace(c,d)).charAt(0)&&(g="-"+g),o+g)),f=r),new f(b,t))};var s=/^data[-\w.:]+$/i,l=/-[a-z]/g,c=/[A-Z]/g;function d(e){return"-"+e.toLowerCase()}function u(e){return e.charAt(1).toUpperCase()}},31872:function(e,t,n){var a=n(96130),r=n(64730),i=n(61861),o=n(46982),s=n(83671),l=n(53618);e.exports=a([i,r,o,s,l])},83671:function(e,t,n){var a=n(7667),r=n(13585),i=a.booleanish,o=a.number,s=a.spaceSeparated;e.exports=r({transform:function(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()},properties:{ariaActiveDescendant:null,ariaAtomic:i,ariaAutoComplete:null,ariaBusy:i,ariaChecked:i,ariaColCount:o,ariaColIndex:o,ariaColSpan:o,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:i,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:i,ariaFlowTo:s,ariaGrabbed:i,ariaHasPopup:null,ariaHidden:i,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:o,ariaLive:null,ariaModal:i,ariaMultiLine:i,ariaMultiSelectable:i,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:o,ariaPressed:i,ariaReadOnly:i,ariaRelevant:null,ariaRequired:i,ariaRoleDescription:s,ariaRowCount:o,ariaRowIndex:o,ariaRowSpan:o,ariaSelected:i,ariaSetSize:o,ariaSort:null,ariaValueMax:o,ariaValueMin:o,ariaValueNow:o,ariaValueText:null,role:null}})},53618:function(e,t,n){var a=n(7667),r=n(13585),i=n(46640),o=a.boolean,s=a.overloadedBoolean,l=a.booleanish,c=a.number,d=a.spaceSeparated,u=a.commaSeparated;e.exports=r({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:i,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:u,acceptCharset:d,accessKey:d,action:null,allow:null,allowFullScreen:o,allowPaymentRequest:o,allowUserMedia:o,alt:null,as:null,async:o,autoCapitalize:null,autoComplete:d,autoFocus:o,autoPlay:o,capture:o,charSet:null,checked:o,cite:null,className:d,cols:c,colSpan:null,content:null,contentEditable:l,controls:o,controlsList:d,coords:c|u,crossOrigin:null,data:null,dateTime:null,decoding:null,default:o,defer:o,dir:null,dirName:null,disabled:o,download:s,draggable:l,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:o,formTarget:null,headers:d,height:c,hidden:o,high:c,href:null,hrefLang:null,htmlFor:d,httpEquiv:d,id:null,imageSizes:null,imageSrcSet:u,inputMode:null,integrity:null,is:null,isMap:o,itemId:null,itemProp:d,itemRef:d,itemScope:o,itemType:d,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:o,low:c,manifest:null,max:null,maxLength:c,media:null,method:null,min:null,minLength:c,multiple:o,muted:o,name:null,nonce:null,noModule:o,noValidate:o,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:o,optimum:c,pattern:null,ping:d,placeholder:null,playsInline:o,poster:null,preload:null,readOnly:o,referrerPolicy:null,rel:d,required:o,reversed:o,rows:c,rowSpan:c,sandbox:d,scope:null,scoped:o,seamless:o,selected:o,shape:null,size:c,sizes:null,slot:null,span:c,spellCheck:l,src:null,srcDoc:null,srcLang:null,srcSet:u,start:c,step:null,style:null,tabIndex:c,target:null,title:null,translate:null,type:null,typeMustMatch:o,useMap:null,value:l,width:c,wrap:null,align:null,aLink:null,archive:d,axis:null,background:null,bgColor:null,border:c,borderColor:null,bottomMargin:c,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:o,declare:o,event:null,face:null,frame:null,frameBorder:null,hSpace:c,leftMargin:c,link:null,longDesc:null,lowSrc:null,marginHeight:c,marginWidth:c,noResize:o,noHref:o,noShade:o,noWrap:o,object:null,profile:null,prompt:null,rev:null,rightMargin:c,rules:null,scheme:null,scrolling:l,standby:null,summary:null,text:null,topMargin:c,valueType:null,version:null,vAlign:null,vLink:null,vSpace:c,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:o,disableRemotePlayback:o,prefix:null,property:null,results:c,security:null,unselectable:null}})},46640:function(e,t,n){var a=n(25852);e.exports=function(e,t){return a(e,t.toLowerCase())}},25852:function(e){e.exports=function(e,t){return t in e?e[t]:t}},13585:function(e,t,n){var a=n(39900),r=n(94949),i=n(7478);e.exports=function(e){var t,n,o=e.space,s=e.mustUseProperty||[],l=e.attributes||{},c=e.properties,d=e.transform,u={},p={};for(t in c)n=new i(t,d(l,t),c[t],o),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),u[t]=n,p[a(t)]=t,p[a(n.attribute)]=t;return new r(u,p,o)}},7478:function(e,t,n){var a=n(74108),r=n(7667);e.exports=s,s.prototype=new a,s.prototype.defined=!0;var i=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],o=i.length;function s(e,t,n,s){var l,c,d,u=-1;for(s&&(this.space=s),a.call(this,e,t);++u